use substrait::proto::expression::{FieldReference, Literal, ScalarFunction};
use substrait::proto::{Expression, Type};
use crate::extensions::SimpleExtensions;
use crate::extensions::simple::ExtensionKind;
use crate::fixtures::TestContext;
use crate::parser::common::test_support::{Parse, ScopedParse};
use crate::textify::foundation::ErrorQueue;
use crate::textify::{OutputOptions, Textify};
fn must_parse<T, E: std::fmt::Display>(result: Result<T, E>, input: &str) -> T {
let errors = ErrorQueue::default();
let t = match result {
Ok(t) => t,
Err(e) => {
println!("Error parsing {input}:\n{e}");
panic!("{}", e);
}
};
errors.errs().expect("Failure while parsing");
t
}
fn roundtrip_parse<T: Parse + Textify + std::fmt::Debug>(ctx: &TestContext, input: &str) {
let t = must_parse(T::parse(input), input);
let actual = ctx.textify_no_errors(&t);
assert_eq!(actual, input);
}
fn assert_roundtrip<T: ScopedParse + Textify + std::fmt::Debug>(ctx: &TestContext, input: &str) {
let t = must_parse(T::parse(&ctx.extensions, input), input);
let actual = ctx.textify_no_errors(&t);
assert_eq!(actual, input);
}
fn roundtrip_with_simple_output<T: ScopedParse + Textify + std::fmt::Debug>(
extensions: SimpleExtensions,
verbose: &str,
simple: &str,
) {
let ctx1 = TestContext {
options: OutputOptions::verbose(),
extensions,
extension_registry: Default::default(),
};
let t = must_parse(T::parse(&ctx1.extensions, verbose), verbose);
let actual = ctx1.textify_no_errors(&t);
assert_eq!(
actual, verbose,
"Expected verbose output: {verbose}, Actual: {actual}"
);
let ctx2 = TestContext {
options: OutputOptions::default(),
extensions: ctx1.extensions,
extension_registry: Default::default(),
};
let actual = ctx2.textify_no_errors(&t);
assert_eq!(
actual, simple,
"Expected simple output: {simple}, Actual: {actual}"
);
}
#[test]
fn test_types() {
let options = OutputOptions::verbose();
let mut extensions = SimpleExtensions::default();
extensions
.add_extension_urn("some_source".to_string(), 4)
.unwrap();
extensions
.add_extension(ExtensionKind::Type, 4, 12, "MyType".to_string())
.unwrap();
let ctx = TestContext {
options,
extensions,
extension_registry: Default::default(),
};
assert_roundtrip::<Type>(&ctx, "i32");
assert_roundtrip::<Type>(&ctx, "i32?");
assert_roundtrip::<Type>(&ctx, "binary");
assert_roundtrip::<Type>(&ctx, "binary?");
assert_roundtrip::<Type>(&ctx, "timestamp");
assert_roundtrip::<Type>(&ctx, "timestamp?");
assert_roundtrip::<Type>(&ctx, "timestamp_tz");
assert_roundtrip::<Type>(&ctx, "timestamp_tz?");
assert_roundtrip::<Type>(&ctx, "date");
assert_roundtrip::<Type>(&ctx, "date?");
assert_roundtrip::<Type>(&ctx, "time");
assert_roundtrip::<Type>(&ctx, "time?");
assert_roundtrip::<Type>(&ctx, "interval_year");
assert_roundtrip::<Type>(&ctx, "interval_year?");
assert_roundtrip::<Type>(&ctx, "uuid");
assert_roundtrip::<Type>(&ctx, "uuid?");
assert_roundtrip::<Type>(&ctx, "MyType#12");
assert_roundtrip::<Type>(&ctx, "MyType#12?");
assert_roundtrip::<Type>(&ctx, "MyType#12<i32>");
assert_roundtrip::<Type>(&ctx, "MyType#12<i32?>");
assert_roundtrip::<Type>(&ctx, "MyType#12<i32, string?>");
assert_roundtrip::<Type>(&ctx, "MyType#12?<i32, string?>");
assert_roundtrip::<Type>(&ctx, "precisiontimestamp<9>");
assert_roundtrip::<Type>(&ctx, "precisiontimestamp?<9>");
assert_roundtrip::<Type>(&ctx, "precisiontimestamptz<6>");
assert_roundtrip::<Type>(&ctx, "precisiontimestamptz?<6>");
assert_roundtrip::<Type>(&ctx, "precisiontime<3>");
assert_roundtrip::<Type>(&ctx, "precisiontime?<3>");
}
#[test]
fn test_expression() {
let options = OutputOptions::default();
let mut extensions = SimpleExtensions::default();
extensions
.add_extension_urn("some_source".to_string(), 4)
.unwrap();
extensions
.add_extension(ExtensionKind::Function, 4, 12, "foo".to_string())
.unwrap();
extensions
.add_extension(ExtensionKind::Function, 4, 14, "bar".to_string())
.unwrap();
let ctx = TestContext {
options,
extensions,
extension_registry: Default::default(),
};
assert_roundtrip::<Literal>(&ctx, "12");
assert_roundtrip::<Literal>(&ctx, "12:i32");
roundtrip_parse::<FieldReference>(&ctx, "$1");
assert_roundtrip::<ScalarFunction>(&ctx, "foo():i64");
assert_roundtrip::<Expression>(&ctx, "bar(12):i64");
assert_roundtrip::<Expression>(&ctx, "foo(bar(12:i16, 18):i64, -4:i16):i64");
assert_roundtrip::<Expression>(&ctx, "foo($2, bar($5, 18):i64, -4:i16):i64");
}
#[test]
fn test_verbose_and_simple_output() {
let mut extensions = SimpleExtensions::default();
extensions
.add_extension_urn("some_source".to_string(), 4)
.unwrap();
extensions
.add_extension(ExtensionKind::Function, 4, 12, "foo".to_string())
.unwrap();
extensions
.add_extension(ExtensionKind::Function, 4, 14, "bar".to_string())
.unwrap();
roundtrip_with_simple_output::<ScalarFunction>(extensions.clone(), "foo#12():i64", "foo():i64");
roundtrip_with_simple_output::<ScalarFunction>(
extensions.clone(),
"foo#12(3:i32, 5:i64):i64",
"foo(3:i32, 5):i64",
);
roundtrip_with_simple_output::<ScalarFunction>(
extensions.clone(),
"foo#12(bar#14(5:i64, $2):i64):i64",
"foo(bar(5, $2):i64):i64",
);
}