substrait_validator/export/
proto.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! This module provides an export format based on protobuf, to represent the
4//! output tree as accurately as possible.
5//!
6//! This is primarily intended to be used to cross programming language
7//! boundaries for the validator output, whenever the simplified formats are
8//! not comprehensive enough. The Python bindings specifically make extensive
9//! use of this.
10
11use crate::input::proto::substrait::validator;
12use crate::output::comment;
13use crate::output::diagnostic;
14use crate::output::extension;
15use crate::output::parse_result;
16use crate::output::path;
17use crate::output::primitive_data;
18use crate::output::tree;
19use crate::output::type_system::data;
20use crate::output::type_system::meta;
21use prost::Message;
22
23impl From<&parse_result::ParseResult> for validator::ParseResult {
24    fn from(result: &parse_result::ParseResult) -> Self {
25        Self {
26            root: Some((&result.root).into()),
27        }
28    }
29}
30
31impl From<&tree::Node> for validator::Node {
32    fn from(node: &tree::Node) -> Self {
33        Self {
34            node_type: Some((&node.node_type).into()),
35            class: (&node.class).into(),
36            brief: node.brief.as_ref().map(|x| x.into()),
37            summary: node.summary.as_ref().map(|x| x.into()),
38            data_type: node.data_type.as_ref().map(|x| x.into()),
39            data: node.data.iter().map(|x| x.into()).collect(),
40        }
41    }
42}
43
44impl From<&tree::Class> for i32 {
45    fn from(class: &tree::Class) -> Self {
46        match class {
47            tree::Class::Misc => validator::node::Class::Unspecified,
48            tree::Class::Type => validator::node::Class::Type,
49            tree::Class::Expression => validator::node::Class::Expression,
50            tree::Class::Relation => validator::node::Class::Relation,
51        }
52        .into()
53    }
54}
55
56impl From<&tree::NodeData> for validator::node::Data {
57    fn from(node: &tree::NodeData) -> Self {
58        Self {
59            kind: Some(match node {
60                tree::NodeData::Child(child) => validator::node::data::Kind::Child(child.into()),
61                tree::NodeData::Diagnostic(diagnostic) => {
62                    validator::node::data::Kind::Diagnostic(diagnostic.into())
63                }
64                tree::NodeData::DataType(data_type) => {
65                    validator::node::data::Kind::DataType(data_type.into())
66                }
67                tree::NodeData::Comment(comment) => {
68                    validator::node::data::Kind::Comment(comment.into())
69                }
70            }),
71        }
72    }
73}
74
75impl From<&tree::Child> for validator::node::Child {
76    fn from(node: &tree::Child) -> Self {
77        Self {
78            path: Some((&node.path_element).into()),
79            node: Some(node.node.as_ref().into()),
80            recognized: node.recognized,
81        }
82    }
83}
84
85impl From<&diagnostic::Diagnostic> for validator::Diagnostic {
86    fn from(node: &diagnostic::Diagnostic) -> Self {
87        Self {
88            original_level: (&node.original_level).into(),
89            adjusted_level: (&node.adjusted_level).into(),
90            cause: node.cause.classification.into(),
91            msg: node.cause.to_string(),
92            path: Some((&node.path).into()),
93        }
94    }
95}
96
97impl From<&diagnostic::Level> for i32 {
98    fn from(node: &diagnostic::Level) -> Self {
99        match node {
100            diagnostic::Level::Error => validator::diagnostic::Level::Error,
101            diagnostic::Level::Warning => validator::diagnostic::Level::Warning,
102            diagnostic::Level::Info => validator::diagnostic::Level::Info,
103        }
104        .into()
105    }
106}
107
108impl From<&comment::Comment> for validator::Comment {
109    fn from(node: &comment::Comment) -> Self {
110        Self {
111            elements: node.elements().iter().map(|x| x.into()).collect(),
112        }
113    }
114}
115
116impl From<&comment::Brief> for validator::Comment {
117    fn from(node: &comment::Brief) -> Self {
118        Self {
119            elements: node
120                .spans()
121                .iter()
122                .map(|x| validator::comment::Element {
123                    kind: Some(validator::comment::element::Kind::Span(x.into())),
124                })
125                .collect(),
126        }
127    }
128}
129
130impl From<&comment::Element> for validator::comment::Element {
131    fn from(node: &comment::Element) -> Self {
132        validator::comment::Element {
133            kind: Some(match node {
134                comment::Element::Span(span) => {
135                    validator::comment::element::Kind::Span(span.into())
136                }
137                comment::Element::NewLine => validator::comment::element::Kind::NewLine(()),
138                comment::Element::ListOpen => validator::comment::element::Kind::ListOpen(()),
139                comment::Element::ListNext => validator::comment::element::Kind::ListNext(()),
140                comment::Element::ListClose => validator::comment::element::Kind::ListClose(()),
141            }),
142        }
143    }
144}
145
146impl From<&comment::Span> for validator::comment::Span {
147    fn from(node: &comment::Span) -> Self {
148        Self {
149            text: node.text.to_string(),
150            link: node.link.as_ref().map(|x| x.into()),
151        }
152    }
153}
154
155impl From<&comment::Link> for validator::comment::span::Link {
156    fn from(node: &comment::Link) -> Self {
157        match node {
158            comment::Link::Path(path) => validator::comment::span::Link::Path(path.into()),
159            comment::Link::Url(url) => validator::comment::span::Link::Url(url.into()),
160        }
161    }
162}
163
164impl From<&tree::NodeType> for validator::node::NodeType {
165    fn from(node: &tree::NodeType) -> Self {
166        match node {
167            tree::NodeType::ProtoMessage(proto_type) => {
168                validator::node::NodeType::ProtoMessage(validator::node::ProtoMessage {
169                    path: proto_type.to_string(),
170                })
171            }
172            tree::NodeType::ProtoPrimitive(proto_type, data) => {
173                validator::node::NodeType::ProtoPrimitive(validator::node::ProtoPrimitive {
174                    path: proto_type.to_string(),
175                    data: Some(data.into()),
176                })
177            }
178            tree::NodeType::ProtoMissingOneOf => validator::node::NodeType::ProtoMissingOneof(()),
179            tree::NodeType::NodeReference(anchor, node) => {
180                validator::node::NodeType::NodeReference(validator::node::NodeReference {
181                    value: *anchor,
182                    path: Some((&node.path).into()),
183                })
184            }
185            tree::NodeType::YamlMap => validator::node::NodeType::YamlMap(()),
186            tree::NodeType::YamlArray => validator::node::NodeType::YamlArray(()),
187            tree::NodeType::YamlPrimitive(data) => {
188                validator::node::NodeType::YamlPrimitive(data.into())
189            }
190            tree::NodeType::ResolvedUri(uri) => validator::node::NodeType::ResolvedUri(uri.clone()),
191            tree::NodeType::AstNode => validator::node::NodeType::AstNode(()),
192        }
193    }
194}
195
196impl From<&primitive_data::PrimitiveData> for validator::node::PrimitiveData {
197    fn from(node: &primitive_data::PrimitiveData) -> Self {
198        Self {
199            data: match node {
200                primitive_data::PrimitiveData::Null => None,
201                primitive_data::PrimitiveData::Bool(x) => {
202                    Some(validator::node::primitive_data::Data::Boolean(*x))
203                }
204                primitive_data::PrimitiveData::Unsigned(x) => {
205                    Some(validator::node::primitive_data::Data::Unsigned(*x))
206                }
207                primitive_data::PrimitiveData::Signed(x) => {
208                    Some(validator::node::primitive_data::Data::Signed(*x))
209                }
210                primitive_data::PrimitiveData::Float(x) => {
211                    Some(validator::node::primitive_data::Data::Real(*x))
212                }
213                primitive_data::PrimitiveData::String(x) => Some(
214                    validator::node::primitive_data::Data::Unicode(x.to_string()),
215                ),
216                primitive_data::PrimitiveData::Bytes(x) => {
217                    Some(validator::node::primitive_data::Data::Binary(x.clone()))
218                }
219                primitive_data::PrimitiveData::Enum(x) => Some(
220                    validator::node::primitive_data::Data::Variant(x.to_string()),
221                ),
222                primitive_data::PrimitiveData::Any(x) => {
223                    Some(validator::node::primitive_data::Data::Any(x.clone()))
224                }
225            },
226        }
227    }
228}
229
230impl From<&path::PathBuf> for validator::Path {
231    fn from(node: &path::PathBuf) -> Self {
232        Self {
233            root: node.root.to_string(),
234            elements: node.elements.iter().map(|x| x.into()).collect(),
235        }
236    }
237}
238
239impl From<&path::PathElement> for validator::path::Element {
240    fn from(node: &path::PathElement) -> Self {
241        Self {
242            kind: Some(match node {
243                path::PathElement::Field(field) => {
244                    validator::path::element::Kind::Field(validator::path::Field {
245                        field: field.to_string(),
246                    })
247                }
248                path::PathElement::Repeated(field, index) => {
249                    validator::path::element::Kind::RepeatedField(validator::path::RepeatedField {
250                        field: field.to_string(),
251                        index: (*index).try_into().unwrap(),
252                    })
253                }
254                path::PathElement::Variant(field, variant) => {
255                    validator::path::element::Kind::OneofField(validator::path::OneOfField {
256                        field: field.to_string(),
257                        variant: variant.to_string(),
258                    })
259                }
260                path::PathElement::Index(index) => {
261                    validator::path::element::Kind::ArrayElement(validator::path::ArrayElement {
262                        index: (*index).try_into().unwrap(),
263                    })
264                }
265            }),
266        }
267    }
268}
269
270impl From<&data::Type> for validator::DataType {
271    fn from(node: &data::Type) -> Self {
272        Self {
273            class: Some(node.class().into()),
274            nullable: node.nullable(),
275            variation: Some(node.variation().into()),
276            parameters: node.parameters().iter().map(|x| x.into()).collect(),
277        }
278    }
279}
280
281impl From<&data::Class> for validator::data_type::Class {
282    fn from(node: &data::Class) -> Self {
283        validator::data_type::Class {
284            kind: Some(match node {
285                data::Class::Simple(simple) => {
286                    validator::data_type::class::Kind::Simple(simple.into())
287                }
288                data::Class::Compound(compound) => {
289                    validator::data_type::class::Kind::Compound(compound.into())
290                }
291                data::Class::UserDefined(user_defined) => {
292                    validator::data_type::class::Kind::UserDefinedType(user_defined.into())
293                }
294                data::Class::Unresolved => validator::data_type::class::Kind::UnresolvedType(()),
295            }),
296        }
297    }
298}
299
300impl From<&data::class::Simple> for i32 {
301    fn from(node: &data::class::Simple) -> Self {
302        match node {
303            data::class::Simple::Boolean => validator::data_type::Simple::Boolean,
304            data::class::Simple::I8 => validator::data_type::Simple::I8,
305            data::class::Simple::I16 => validator::data_type::Simple::I16,
306            data::class::Simple::I32 => validator::data_type::Simple::I32,
307            data::class::Simple::I64 => validator::data_type::Simple::I64,
308            data::class::Simple::Fp32 => validator::data_type::Simple::Fp32,
309            data::class::Simple::Fp64 => validator::data_type::Simple::Fp64,
310            data::class::Simple::String => validator::data_type::Simple::String,
311            data::class::Simple::Binary => validator::data_type::Simple::Binary,
312            data::class::Simple::Timestamp => validator::data_type::Simple::Timestamp,
313            data::class::Simple::TimestampTz => validator::data_type::Simple::TimestampTz,
314            data::class::Simple::Date => validator::data_type::Simple::Date,
315            data::class::Simple::Time => validator::data_type::Simple::Time,
316            data::class::Simple::IntervalYear => validator::data_type::Simple::IntervalYear,
317            data::class::Simple::IntervalDay => validator::data_type::Simple::IntervalDay,
318            data::class::Simple::Uuid => validator::data_type::Simple::Uuid,
319        }
320        .into()
321    }
322}
323
324impl From<&data::class::Compound> for i32 {
325    fn from(node: &data::class::Compound) -> Self {
326        match node {
327            data::class::Compound::FixedChar => validator::data_type::Compound::FixedChar,
328            data::class::Compound::VarChar => validator::data_type::Compound::VarChar,
329            data::class::Compound::FixedBinary => validator::data_type::Compound::FixedBinary,
330            data::class::Compound::Decimal => validator::data_type::Compound::Decimal,
331            data::class::Compound::Struct => validator::data_type::Compound::Struct,
332            data::class::Compound::NamedStruct => validator::data_type::Compound::NamedStruct,
333            data::class::Compound::List => validator::data_type::Compound::List,
334            data::class::Compound::Map => validator::data_type::Compound::Map,
335        }
336        .into()
337    }
338}
339
340impl From<&extension::simple::type_class::Reference> for validator::data_type::UserDefinedType {
341    fn from(node: &extension::simple::type_class::Reference) -> Self {
342        #[allow(deprecated)]
343        Self {
344            uri: node.uri.name().unwrap_or_default().to_string(),
345            name: node.name.name().unwrap_or_default().to_string(),
346            definition: None,
347            extension_id: node
348                .definition
349                .as_ref()
350                .map(|x| x.extension_id)
351                .unwrap_or_default(),
352        }
353    }
354}
355
356impl From<&extension::simple::type_class::Definition>
357    for validator::data_type::user_defined_type::Definition
358{
359    fn from(node: &extension::simple::type_class::Definition) -> Self {
360        Self {
361            structure: node
362                .structure
363                .iter()
364                .map(
365                    |(name, simple)| validator::data_type::user_defined_type::Element {
366                        name: name.to_string(),
367                        kind: simple.into(),
368                    },
369                )
370                .collect(),
371        }
372    }
373}
374
375impl From<&data::Variation> for validator::data_type::Variation {
376    fn from(node: &data::Variation) -> Self {
377        match node {
378            data::Variation::SystemPreferred => {
379                validator::data_type::Variation::SystemPreferredVariation(())
380            }
381            data::Variation::UserDefined(variation) => {
382                validator::data_type::Variation::UserDefinedVariation(
383                    #[allow(deprecated)]
384                    validator::data_type::UserDefinedVariation {
385                        uri: variation.uri.name().unwrap_or_default().to_string(),
386                        name: variation.name.name().unwrap_or_default().to_string(),
387                        definition: None,
388                        extension_id: variation
389                            .definition
390                            .as_ref()
391                            .map(|x| x.extension_id)
392                            .unwrap_or_default(),
393                    },
394                )
395            }
396        }
397    }
398}
399
400impl From<&data::Parameter> for validator::data_type::Parameter {
401    fn from(node: &data::Parameter) -> Self {
402        Self {
403            name: node.name.clone().unwrap_or_default(),
404            kind: Some(match &node.value {
405                None => validator::data_type::parameter::Kind::Null(()),
406                Some(meta::Value::Unresolved) => {
407                    validator::data_type::parameter::Kind::Unresolved(())
408                }
409                Some(meta::Value::Boolean(x)) => validator::data_type::parameter::Kind::Boolean(*x),
410                Some(meta::Value::Integer(x)) => validator::data_type::parameter::Kind::Integer(*x),
411                Some(meta::Value::Enum(x)) => {
412                    validator::data_type::parameter::Kind::Enumeration(x.clone())
413                }
414                Some(meta::Value::String(x)) => {
415                    validator::data_type::parameter::Kind::String(x.clone())
416                }
417                Some(meta::Value::DataType(data_type)) => {
418                    validator::data_type::parameter::Kind::DataType(data_type.into())
419                }
420            }),
421        }
422    }
423}
424
425/// Export the complete parse tree in protobuf substrait.validator.Node format.
426pub fn export<T: std::io::Write>(
427    out: &mut T,
428    _root_name: &'static str,
429    result: &parse_result::ParseResult,
430) -> std::io::Result<()> {
431    let root = validator::ParseResult::from(result);
432    let buf = root.encode_to_vec();
433    if out.write(&buf)? < buf.len() {
434        Err(std::io::Error::other("failed to write all bytes"))
435    } else {
436        Ok(())
437    }
438}