use crate::input::proto::substrait;
use crate::output::diagnostic;
use crate::output::type_system::data;
use crate::parse::context;
use crate::parse::expressions::literals;
use crate::parse::expressions::references;
use crate::parse::types;
use crate::util;
fn parse_struct_field(
x: &substrait::expression::reference_segment::StructField,
y: &mut context::Context,
root: &data::Type,
) -> diagnostic::Result<references::ReferencePath> {
if !root.is_unresolved() && !root.is_struct() {
diagnostic!(
y,
Error,
TypeMismatch,
"struct selection requires a struct type, but got a {}",
root.class()
);
}
let description = format!(".{}", x.field);
let data_type = proto_primitive_field!(x, y, field, super::parse_struct_field_index, root)
.1
.unwrap_or_default();
let data_type = if root.nullable() {
data_type.make_nullable()
} else {
data_type
};
y.set_data_type(data_type.clone());
let reference = if x.child.is_some() {
let (node, result) =
proto_boxed_required_field!(x, y, child, parse_reference_segment, &data_type);
y.set_data_type(node.data_type());
result.unwrap_or_default().prefix(description)
} else {
references::ReferencePath::new().prefix(description)
};
describe!(y, Expression, "Selects {}", &reference);
summary!(y, "Full reference path: {:#}", &reference);
Ok(reference)
}
fn parse_list_element(
x: &substrait::expression::reference_segment::ListElement,
y: &mut context::Context,
root: &data::Type,
) -> diagnostic::Result<references::ReferencePath> {
if !root.is_unresolved() && !root.is_list() {
diagnostic!(
y,
Error,
TypeMismatch,
"list selection requires a list type, but got a {}",
root.class()
);
}
proto_primitive_field!(x, y, offset, |x, y| {
describe!(
y,
Misc,
"Selects {} list element",
util::string::describe_index(*x)
);
Ok(())
});
let description = format!(".[{}]", x.offset);
let data_type = root.unwrap_list().unwrap_or_default();
let data_type = if root.nullable() {
data_type.make_nullable()
} else {
data_type
};
y.set_data_type(data_type.clone());
let reference = if x.child.is_some() {
let (node, result) =
proto_boxed_required_field!(x, y, child, parse_reference_segment, &data_type);
y.set_data_type(node.data_type());
result.unwrap_or_default().prefix(description)
} else {
references::ReferencePath::new().prefix(description)
};
describe!(y, Expression, "Selects {}", &reference);
summary!(y, "Full reference path: {:#}", &reference);
Ok(reference)
}
fn parse_map_key(
x: &substrait::expression::reference_segment::MapKey,
y: &mut context::Context,
root: &data::Type,
) -> diagnostic::Result<references::ReferencePath> {
if !root.is_unresolved() && !root.is_map() {
diagnostic!(
y,
Error,
TypeMismatch,
"map selection requires a map type, but got a {}",
root.class()
);
}
let key = proto_required_field!(x, y, map_key, literals::parse_literal)
.1
.unwrap_or_default();
types::assert_equal(
y,
key.data_type(),
&root.unwrap_map_key().unwrap_or_default(),
"map key type mismatch",
);
let description = format!(".[{}]", key);
let data_type = root.unwrap_map().unwrap_or_default();
let data_type = if root.nullable() {
data_type.make_nullable()
} else {
data_type
};
y.set_data_type(data_type.clone());
let reference = if x.child.is_some() {
let (node, result) =
proto_boxed_required_field!(x, y, child, parse_reference_segment, &data_type);
y.set_data_type(node.data_type());
result.unwrap_or_default().prefix(description)
} else {
references::ReferencePath::new().prefix(description)
};
describe!(y, Expression, "Selects {}", &reference);
summary!(y, "Full reference path: {:#}", &reference);
Ok(reference)
}
fn parse_reference_type(
x: &substrait::expression::reference_segment::ReferenceType,
y: &mut context::Context,
root: &data::Type,
) -> diagnostic::Result<references::ReferencePath> {
match x {
substrait::expression::reference_segment::ReferenceType::StructField(x) => {
parse_struct_field(x, y, root)
}
substrait::expression::reference_segment::ReferenceType::ListElement(x) => {
parse_list_element(x, y, root)
}
substrait::expression::reference_segment::ReferenceType::MapKey(x) => {
parse_map_key(x, y, root)
}
}
}
pub fn parse_reference_segment(
x: &substrait::expression::ReferenceSegment,
y: &mut context::Context,
root: &data::Type,
) -> diagnostic::Result<references::ReferencePath> {
let (node, result) = proto_required_field!(x, y, reference_type, parse_reference_type, root);
y.set_data_type(node.data_type());
let reference = result.unwrap_or_default();
describe!(y, Expression, "Selects {}", &reference);
summary!(y, "Full reference path: {:#}", &reference);
Ok(reference)
}