diff --git a/rust/cubesql/cubesql/src/compile/rewrite/language.rs b/rust/cubesql/cubesql/src/compile/rewrite/language.rs index 1bae740891932..f8cf0204485e4 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/language.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/language.rs @@ -1,3 +1,34 @@ +use std::{ + num::{ParseFloatError, ParseIntError}, + str::ParseBoolError, +}; + +// `from_op` is generated as if-else chain from op.parse() +// Because of this alot of instances of FromStr::Err wil lbe constructed just as a "take other branch" marker +// This type should be very cheap to construct new instances, at least while we use FromStr chains +// Don't store any input in here, FromStr is not designed for this, and it requires allocation +// Instead rely on egg::FromOpError for now, it will return allocated `op`, but only once in the end +// TODO make parser dispatch stricter, and return both input and LanguageParseError from `from_op` +#[derive(thiserror::Error, Debug)] +pub enum LanguageParseError { + #[error("Should start with '{0}'")] + ShouldStartWith(&'static str), + #[error("Can't be matched against {0}")] + ShouldMatch(&'static str), + #[error("Should contain a valid type")] + InvalidType, + #[error("Should contains a valid scalar type")] + InvalidScalarType, + #[error("Can't parse boolean scalar value with error: {0}")] + InvalidBoolValue(#[source] ParseBoolError), + #[error("Can't parse i64 scalar value with error: {0}")] + InvalidIntValue(#[source] ParseIntError), + #[error("Can't parse f64 scalar value with error: {0}")] + InvalidFloatValue(#[source] ParseFloatError), + #[error("Conversion from string is not supported")] + NotSupported, +} + #[macro_export] macro_rules! plan_to_language { ($(#[$meta:meta])* $vis:vis enum $name:ident $variants:tt) => { @@ -13,13 +44,13 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](String); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(s: &str) -> Result { - let prefix = format!("{}:", std::stringify!([<$variant $var_field:camel>])); - if s.starts_with(&prefix) { - return Ok([<$variant $var_field:camel>](s.replace(&prefix, ""))); + const PREFIX: &'static str = concat!(std::stringify!([<$variant $var_field:camel>]), ":"); + if let Some(suffix) = s.strip_prefix(PREFIX) { + return Ok([<$variant $var_field:camel>](suffix.to_string())); } - Err(CubeError::internal(format!("Can't convert {}. Should start with '{}'", s, prefix))) + Err(Self::Err::ShouldStartWith(PREFIX)) } } @@ -37,13 +68,13 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](usize); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(s: &str) -> Result { - let prefix = format!("{}:", std::stringify!([<$variant $var_field:camel>])); - if s.starts_with(&prefix) { - return Ok([<$variant $var_field:camel>](s.replace(&prefix, "").parse().unwrap())); + const PREFIX: &'static str = concat!(std::stringify!([<$variant $var_field:camel>]), ":"); + if let Some(suffix) = s.strip_prefix(PREFIX) { + return Ok([<$variant $var_field:camel>](suffix.parse().unwrap())); } - Err(CubeError::internal(format!("Can't convert {}. Should start with '{}'", s, prefix))) + Err(Self::Err::ShouldStartWith(PREFIX)) } } @@ -61,13 +92,13 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](bool); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(s: &str) -> Result { - let prefix = format!("{}:", std::stringify!([<$variant $var_field:camel>])); - if s.starts_with(&prefix) { - return Ok([<$variant $var_field:camel>](s.replace(&prefix, "").parse().unwrap())); + const PREFIX: &'static str = concat!(std::stringify!([<$variant $var_field:camel>]), ":"); + if let Some(suffix) = s.strip_prefix(PREFIX) { + return Ok([<$variant $var_field:camel>](suffix.parse().unwrap())); } - Err(CubeError::internal(format!("Can't convert {}. Should start with '{}'", s, prefix))) + Err(Self::Err::ShouldStartWith(PREFIX)) } } @@ -85,18 +116,17 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](Option); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(s: &str) -> Result { - let prefix = format!("{}:", std::stringify!([<$variant $var_field:camel>])); - if s.starts_with(&prefix) { - let replaced = s.replace(&prefix, ""); - if &replaced == "None" { + const PREFIX: &'static str = concat!(std::stringify!([<$variant $var_field:camel>]), ":"); + if let Some(suffix) = s.strip_prefix(PREFIX) { + if suffix == "None" { return Ok([<$variant $var_field:camel>](None)); } else { - return Ok([<$variant $var_field:camel>](Some(s.replace(&prefix, "").parse().unwrap()))); + return Ok([<$variant $var_field:camel>](Some(suffix.parse().unwrap()))); } } - Err(CubeError::internal(format!("Can't convert {}. Should start with '{}'", s, prefix))) + Err(Self::Err::ShouldStartWith(PREFIX)) } } @@ -114,18 +144,17 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](Option>); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(s: &str) -> Result { - let prefix = format!("{}:", std::stringify!([<$variant $var_field:camel>])); - if s.starts_with(&prefix) { - let replaced = s.replace(&prefix, ""); - if &replaced == "None" { + const PREFIX: &'static str = concat!(std::stringify!([<$variant $var_field:camel>]), ":"); + if let Some(suffix) = s.strip_prefix(PREFIX) { + if suffix == "None" { return Ok([<$variant $var_field:camel>](None)); } else { - return Ok([<$variant $var_field:camel>](Some(s.split(',').map(|s| s.to_string()).collect::>()))); + return Ok([<$variant $var_field:camel>](Some(suffix.split(',').map(|s| s.to_string()).collect::>()))); } } - Err(CubeError::internal(format!("Can't convert {}. Should start with '{}'", s, prefix))) + Err(Self::Err::ShouldStartWith(PREFIX)) } } @@ -143,18 +172,17 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](Option); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(s: &str) -> Result { - let prefix = format!("{}:", std::stringify!([<$variant $var_field:camel>])); - if s.starts_with(&prefix) { - let replaced = s.replace(&prefix, ""); - if &replaced == "None" { + const PREFIX: &'static str = concat!(std::stringify!([<$variant $var_field:camel>]), ":"); + if let Some(suffix) = s.strip_prefix(PREFIX) { + if suffix == "None" { return Ok([<$variant $var_field:camel>](None)); } else { - return Ok([<$variant $var_field:camel>](Some(s.to_string()))); + return Ok([<$variant $var_field:camel>](Some(suffix.to_string()))); } } - Err(CubeError::internal("Conversion from string is not supported".to_string())) + Err(Self::Err::NotSupported) } } @@ -172,9 +200,9 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](Column); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(_s: &str) -> Result { - Err(CubeError::internal("Conversion from string is not supported".to_string())) + Err(Self::Err::NotSupported) } } @@ -192,9 +220,9 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](Vec); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(_s: &str) -> Result { - Err(CubeError::internal("Conversion from string is not supported".to_string())) + Err(Self::Err::NotSupported) } } @@ -212,9 +240,9 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](Vec); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(_s: &str) -> Result { - Err(CubeError::internal("Conversion from string is not supported".to_string())) + Err(Self::Err::NotSupported) } } @@ -232,9 +260,9 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](String); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(_s: &str) -> Result { - Err(CubeError::internal("Conversion from string is not supported".to_string())) + Err(Self::Err::NotSupported) } } @@ -252,9 +280,9 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](String); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(_s: &str) -> Result { - Err(CubeError::internal("Conversion from string is not supported".to_string())) + Err(Self::Err::NotSupported) } } @@ -272,13 +300,13 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](String); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(s: &str) -> Result { - let prefix = format!("{}:", std::stringify!([<$variant $var_field:camel>])); - if s.starts_with(&prefix) { - return Ok([<$variant $var_field:camel>](s.replace(&prefix, ""))); + const PREFIX: &'static str = concat!(std::stringify!([<$variant $var_field:camel>]), ":"); + if let Some(suffix) = s.strip_prefix(PREFIX) { + return Ok([<$variant $var_field:camel>](suffix.to_string())); } - Err(CubeError::internal(format!("Can't convert {}. Should start with '{}'", s, prefix))) + Err(Self::Err::ShouldStartWith(PREFIX)) } } @@ -484,14 +512,16 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>]($var_field_type); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(s: &str) -> Result { - let prefix = format!("{}:", std::stringify!([<$variant $var_field:camel>])); - let name = s.strip_prefix(&prefix).ok_or(CubeError::internal(format!("Can't convert {}. Should start with '{}'", s, prefix)))?; + const PREFIX: &'static str = concat!(std::stringify!([<$variant $var_field:camel>]), ":"); + let name = s + .strip_prefix(PREFIX) + .ok_or(Self::Err::ShouldStartWith(PREFIX))?; match name { $($name => Ok([<$variant $var_field:camel>]($variant_type)),)* - x => Err(CubeError::internal(format!("{} can't be matched against {}", x, std::stringify!($var_field_type)))) + _ => Err(Self::Err::ShouldMatch(std::stringify!($var_field_type))) } } } @@ -557,10 +587,12 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](DataType); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(s: &str) -> Result { - let prefix = format!("{}:", std::stringify!([<$variant $var_field:camel>])); - let typed_str = s.strip_prefix(&prefix).ok_or(CubeError::internal(format!("Can't convert {}. Should start with '{}'", s, prefix)))?; + const PREFIX: &'static str = concat!(std::stringify!([<$variant $var_field:camel>]), ":"); + let typed_str = s + .strip_prefix(PREFIX) + .ok_or(Self::Err::ShouldStartWith(PREFIX))?; match typed_str { "Float32" => Ok([<$variant $var_field:camel>](DataType::Float32)), @@ -571,7 +603,7 @@ macro_rules! variant_field_struct { "Utf8" => Ok([<$variant $var_field:camel>](DataType::Utf8)), "Date32" => Ok([<$variant $var_field:camel>](DataType::Date32)), "Date64" => Ok([<$variant $var_field:camel>](DataType::Date64)), - _ => Err(CubeError::internal(format!("Can't convert {}. Should contain a valid type, actual: {}", s, typed_str))), + _ => Err(Self::Err::InvalidType), } } } @@ -590,24 +622,26 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>](ScalarValue); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(s: &str) -> Result { - let prefix = format!("{}:", std::stringify!([<$variant $var_field:camel>])); - let typed_str = s.strip_prefix(&prefix).ok_or(CubeError::internal(format!("Can't convert {}. Should start with '{}'", s, prefix)))?; + const PREFIX: &'static str = concat!(std::stringify!([<$variant $var_field:camel>]), ":"); + let typed_str = s + .strip_prefix(PREFIX) + .ok_or(Self::Err::ShouldStartWith(PREFIX))?; if let Some(value) = typed_str.strip_prefix("s:") { Ok([<$variant $var_field:camel>](ScalarValue::Utf8(Some(value.to_string())))) } else if let Some(value) = typed_str.strip_prefix("b:") { - let n: bool = value.parse().map_err(|err| CubeError::internal(format!("Can't parse boolean scalar value from '{}' with error: {}", typed_str, err)))?; + let n: bool = value.parse().map_err(|err| Self::Err::InvalidBoolValue(err))?; Ok([<$variant $var_field:camel>](ScalarValue::Boolean(Some(n)))) } else if let Some(value) = typed_str.strip_prefix("i:") { - let n: i64 = value.parse().map_err(|err| CubeError::internal(format!("Can't parse i64 scalar value from '{}' with error: {}", typed_str, err)))?; + let n: i64 = value.parse().map_err(|err| Self::Err::InvalidIntValue(err))?; Ok([<$variant $var_field:camel>](ScalarValue::Int64(Some(n)))) } else if let Some(value) = typed_str.strip_prefix("f:") { - let n: f64 = value.parse().map_err(|err| CubeError::internal(format!("Can't parse f64 scalar value from '{}' with error: {}", typed_str, err)))?; + let n: f64 = value.parse().map_err(|err| Self::Err::InvalidFloatValue(err))?; Ok([<$variant $var_field:camel>](ScalarValue::Float64(Some(n)))) } else { - Err(CubeError::internal(format!("Can't convert {}. Should contains type type, actual: {}", s, typed_str))) + Err(Self::Err::InvalidScalarType) } } } @@ -657,9 +691,9 @@ macro_rules! variant_field_struct { pub struct [<$variant $var_field:camel>]($var_field_type); impl FromStr for [<$variant $var_field:camel>] { - type Err = CubeError; + type Err = $crate::compile::rewrite::language::LanguageParseError; fn from_str(_s: &str) -> Result { - Err(CubeError::internal("Conversion from string is not supported".to_string())) + Err(Self::Err::NotSupported) } } diff --git a/rust/cubesql/cubesql/src/compile/rewrite/mod.rs b/rust/cubesql/cubesql/src/compile/rewrite/mod.rs index c368ef8c1f4c1..0654ecc904d0b 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/mod.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/mod.rs @@ -6,12 +6,9 @@ pub mod rewriter; pub mod rules; use self::analysis::{LogicalPlanData, MemberNameToExpr}; -use crate::{ - compile::rewrite::{ - analysis::{LogicalPlanAnalysis, OriginalExpr}, - rewriter::{CubeEGraph, CubeRewrite}, - }, - CubeError, +use crate::compile::rewrite::{ + analysis::{LogicalPlanAnalysis, OriginalExpr}, + rewriter::{CubeEGraph, CubeRewrite}, }; use analysis::MemberNamesToExpr; use datafusion::{