diff --git a/src/backend/mod.rs b/src/backend/mod.rs index ebbd27bd2..f220fdf4c 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,6 +1,7 @@ //! Translating the SQL AST into engine-specific SQL statements. use crate::*; +use std::fmt::Write; #[cfg(feature = "backend-mysql")] #[cfg_attr(docsrs, doc(cfg(feature = "backend-mysql")))] diff --git a/src/backend/mysql/table.rs b/src/backend/mysql/table.rs index 2a81f921c..408b1809b 100644 --- a/src/backend/mysql/table.rs +++ b/src/backend/mysql/table.rs @@ -122,7 +122,7 @@ impl TableBuilder for MysqlQueryBuilder { match column_spec { ColumnSpec::Null => write!(sql, "NULL"), ColumnSpec::NotNull => write!(sql, "NOT NULL"), - ColumnSpec::Default(value) => write!(sql, "DEFAULT {}", self.value_to_string(value)), + ColumnSpec::Default(value) => write!(sql, "DEFAULT {}", value.to_sql_string()), ColumnSpec::AutoIncrement => write!(sql, "AUTO_INCREMENT"), ColumnSpec::UniqueKey => write!(sql, "UNIQUE"), ColumnSpec::PrimaryKey => write!(sql, "PRIMARY KEY"), diff --git a/src/backend/postgres/table.rs b/src/backend/postgres/table.rs index e80a99db8..bc5e4b8da 100644 --- a/src/backend/postgres/table.rs +++ b/src/backend/postgres/table.rs @@ -111,9 +111,7 @@ impl TableBuilder for PostgresQueryBuilder { match column_spec { ColumnSpec::Null => write!(sql, "NULL"), ColumnSpec::NotNull => write!(sql, "NOT NULL"), - ColumnSpec::Default(value) => { - write!(sql, "DEFAULT {}", self.value_to_string(value)) - } + ColumnSpec::Default(value) => write!(sql, "DEFAULT {}", value.to_sql_string()), ColumnSpec::AutoIncrement => write!(sql, ""), ColumnSpec::UniqueKey => write!(sql, "UNIQUE"), ColumnSpec::PrimaryKey => write!(sql, "PRIMARY KEY"), diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index d71367839..15da2835c 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -958,8 +958,7 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder { write!(sql, " WHEN ").unwrap(); self.prepare_simple_expr(&order_expr.expr, sql, collector); write!(sql, "=").unwrap(); - let value = self.value_to_string(value); - write!(sql, "{}", value).unwrap(); + write!(sql, "{}", value.to_sql_string()).unwrap(); write!(sql, " THEN {} ", i).unwrap(); i += 1; } @@ -975,8 +974,7 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder { /// Write [`Value`] inline. fn prepare_constant(&self, value: &Value, sql: &mut SqlWriter) { - let string = self.value_to_string(value); - write!(sql, "{}", string).unwrap(); + write!(sql, "{}", value.to_sql_string()).unwrap(); } /// Translate a `&[ValueTuple]` into a VALUES list. @@ -1048,144 +1046,6 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder { } } - /// Convert a SQL value into syntax-specific string - fn value_to_string(&self, v: &Value) -> String { - let mut s = String::new(); - match v { - Value::Bool(None) - | Value::TinyInt(None) - | Value::SmallInt(None) - | Value::Int(None) - | Value::BigInt(None) - | Value::TinyUnsigned(None) - | Value::SmallUnsigned(None) - | Value::Unsigned(None) - | Value::BigUnsigned(None) - | Value::Float(None) - | Value::Double(None) - | Value::String(None) - | Value::Char(None) - | Value::Bytes(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-json")] - Value::Json(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDate(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoTime(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTime(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeUtc(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeLocal(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeWithTimeZone(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-time")] - Value::TimeDate(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-time")] - Value::TimeTime(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-time")] - Value::TimeDateTime(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-time")] - Value::TimeDateTimeWithTimeZone(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-rust_decimal")] - Value::Decimal(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-bigdecimal")] - Value::BigDecimal(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-uuid")] - Value::Uuid(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-mac_address")] - Value::MacAddress(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "postgres-array")] - Value::Array(None) => write!(s, "NULL").unwrap(), - Value::Bool(Some(b)) => write!(s, "{}", if *b { "TRUE" } else { "FALSE" }).unwrap(), - Value::TinyInt(Some(v)) => write!(s, "{}", v).unwrap(), - Value::SmallInt(Some(v)) => write!(s, "{}", v).unwrap(), - Value::Int(Some(v)) => write!(s, "{}", v).unwrap(), - Value::BigInt(Some(v)) => write!(s, "{}", v).unwrap(), - Value::TinyUnsigned(Some(v)) => write!(s, "{}", v).unwrap(), - Value::SmallUnsigned(Some(v)) => write!(s, "{}", v).unwrap(), - Value::Unsigned(Some(v)) => write!(s, "{}", v).unwrap(), - Value::BigUnsigned(Some(v)) => write!(s, "{}", v).unwrap(), - Value::Float(Some(v)) => write!(s, "{}", v).unwrap(), - Value::Double(Some(v)) => write!(s, "{}", v).unwrap(), - Value::String(Some(v)) => self.write_string_quoted(v, &mut s), - Value::Char(Some(v)) => { - self.write_string_quoted(std::str::from_utf8(&[*v as u8]).unwrap(), &mut s) - } - Value::Bytes(Some(v)) => write!( - s, - "x'{}'", - v.iter().map(|b| format!("{:02X}", b)).collect::() - ) - .unwrap(), - #[cfg(feature = "with-json")] - Value::Json(Some(v)) => self.write_string_quoted(&v.to_string(), &mut s), - #[cfg(feature = "with-chrono")] - Value::ChronoDate(Some(v)) => write!(s, "'{}'", v.format("%Y-%m-%d")).unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoTime(Some(v)) => write!(s, "'{}'", v.format("%H:%M:%S")).unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTime(Some(v)) => { - write!(s, "'{}'", v.format("%Y-%m-%d %H:%M:%S")).unwrap() - } - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeUtc(Some(v)) => { - write!(s, "'{}'", v.format("%Y-%m-%d %H:%M:%S %:z")).unwrap() - } - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeLocal(Some(v)) => { - write!(s, "'{}'", v.format("%Y-%m-%d %H:%M:%S %:z")).unwrap() - } - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeWithTimeZone(Some(v)) => { - write!(s, "'{}'", v.format("%Y-%m-%d %H:%M:%S %:z")).unwrap() - } - #[cfg(feature = "with-time")] - Value::TimeDate(Some(v)) => { - write!(s, "'{}'", v.format(time_format::FORMAT_DATE).unwrap()).unwrap() - } - #[cfg(feature = "with-time")] - Value::TimeTime(Some(v)) => { - write!(s, "'{}'", v.format(time_format::FORMAT_TIME).unwrap()).unwrap() - } - #[cfg(feature = "with-time")] - Value::TimeDateTime(Some(v)) => { - write!(s, "'{}'", v.format(time_format::FORMAT_DATETIME).unwrap()).unwrap() - } - #[cfg(feature = "with-time")] - Value::TimeDateTimeWithTimeZone(Some(v)) => write!( - s, - "'{}'", - v.format(time_format::FORMAT_DATETIME_TZ).unwrap() - ) - .unwrap(), - #[cfg(feature = "with-rust_decimal")] - Value::Decimal(Some(v)) => write!(s, "{}", v).unwrap(), - #[cfg(feature = "with-bigdecimal")] - Value::BigDecimal(Some(v)) => write!(s, "{}", v).unwrap(), - #[cfg(feature = "with-uuid")] - Value::Uuid(Some(v)) => write!(s, "'{}'", v).unwrap(), - #[cfg(feature = "postgres-array")] - Value::Array(Some(v)) => write!( - s, - "'{{{}}}'", - v.iter() - .map(|element| self.value_to_string(element)) - .collect::>() - .join(",") - ) - .unwrap(), - #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(Some(v)) => write!(s, "'{}'", v).unwrap(), - #[cfg(feature = "with-mac_address")] - Value::MacAddress(Some(v)) => write!(s, "'{}'", v).unwrap(), - }; - s - } - #[doc(hidden)] /// Write ON CONFLICT expression fn prepare_on_conflict( @@ -1414,12 +1274,12 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder { match *frame { Frame::UnboundedPreceding => write!(sql, " UNBOUNDED PRECEDING ").unwrap(), Frame::Preceding(v) => { - self.prepare_value(&Some(v).into(), sql, collector); + self.prepare_value(&v.into(), sql, collector); write!(sql, " PRECEDING ").unwrap(); } Frame::CurrentRow => write!(sql, " CURRENT ROW ").unwrap(), Frame::Following(v) => { - self.prepare_value(&Some(v).into(), sql, collector); + self.prepare_value(&v.into(), sql, collector); write!(sql, " FOLLOWING ").unwrap(); } Frame::UnboundedFollowing => write!(sql, " UNBOUNDED FOLLOWING ").unwrap(), diff --git a/src/backend/sqlite/table.rs b/src/backend/sqlite/table.rs index 753a78937..35fc33b7a 100644 --- a/src/backend/sqlite/table.rs +++ b/src/backend/sqlite/table.rs @@ -125,7 +125,7 @@ impl TableBuilder for SqliteQueryBuilder { match column_spec { ColumnSpec::Null => write!(sql, "NULL"), ColumnSpec::NotNull => write!(sql, "NOT NULL"), - ColumnSpec::Default(value) => write!(sql, "DEFAULT {}", self.value_to_string(value)), + ColumnSpec::Default(value) => write!(sql, "DEFAULT {}", value.to_sql_string()), ColumnSpec::AutoIncrement => write!(sql, "AUTOINCREMENT"), ColumnSpec::UniqueKey => write!(sql, "UNIQUE"), ColumnSpec::PrimaryKey => write!(sql, "PRIMARY KEY"), diff --git a/src/expr.rs b/src/expr.rs index 106326654..a34149cda 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1159,14 +1159,14 @@ impl Expr { } fn like_like(self, op: BinOper, like: LikeExpr) -> SimpleExpr { - let value = SimpleExpr::Value(Value::String(Some(Box::new(like.pattern)))); + let value = SimpleExpr::Value(like.pattern.into()); self.bin_oper( op, match like.escape { Some(escape) => SimpleExpr::Binary( Box::new(value), BinOper::Escape, - Box::new(SimpleExpr::Constant(Value::Char(Some(escape)))), + Box::new(SimpleExpr::Constant(escape.into())), ), None => value, }, diff --git a/src/lib.rs b/src/lib.rs index dea51faf8..1a29acb4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -746,6 +746,30 @@ html_logo_url = "https://raw.githubusercontent.com/SeaQL/sea-query/master/docs/SeaQL icon dark.png" )] +pub use backend::*; +pub use driver::*; +// pub use error::*; +pub use expr::*; +//pub use extension::*; +pub use foreign_key::*; +pub use func::*; +pub use index::*; +pub use prepare::*; +pub use query::*; +pub use schema::*; +#[cfg(feature = "attr")] +pub use sea_query_attr::enum_def; +#[cfg(feature = "derive")] +pub use sea_query_derive::Iden; +#[cfg(feature = "sea-query-driver")] +pub use sea_query_driver::*; +pub use table::*; +//pub use shim::*; +//pub use tests_cfg::*; +pub use token::*; +pub use types::*; +pub use value::*; + pub mod backend; pub mod driver; pub mod error; @@ -763,30 +787,3 @@ pub mod tests_cfg; pub mod token; pub mod types; pub mod value; - -pub use backend::*; -pub use driver::*; -//pub use extension::*; -pub use foreign_key::*; -pub use index::*; -pub use query::*; -pub use table::*; -// pub use error::*; -pub use expr::*; -pub use func::*; -pub use prepare::*; -pub use schema::*; -//pub use shim::*; -//pub use tests_cfg::*; -pub use token::*; -pub use types::*; -pub use value::*; - -#[cfg(feature = "derive")] -pub use sea_query_derive::Iden; - -#[cfg(feature = "attr")] -pub use sea_query_attr::enum_def; - -#[cfg(feature = "sea-query-driver")] -pub use sea_query_driver::*; diff --git a/src/prepare.rs b/src/prepare.rs index de8b651a7..6a90335d5 100644 --- a/src/prepare.rs +++ b/src/prepare.rs @@ -1,7 +1,7 @@ //! Helper for preparing SQL statements. -use crate::*; -pub use std::fmt::Write; +use crate::{QueryBuilder, Token, Tokenizer, Value}; +pub use std::fmt::{self, Write}; #[derive(Debug, Default)] pub struct SqlWriter { @@ -24,7 +24,7 @@ where match token { Token::Punctuation(mark) => { if (mark.as_ref(), false) == query_builder.placeholder() { - output.push(query_builder.value_to_string(¶ms[counter])); + output.push(params[counter].to_sql_string()); counter += 1; i += 1; continue; @@ -33,7 +33,7 @@ where { if let Token::Unquoted(next) = &tokens[i + 1] { if let Ok(num) = next.parse::() { - output.push(query_builder.value_to_string(¶ms[num - 1])); + output.push(params[num - 1].to_sql_string()); i += 2; continue; } @@ -79,8 +79,8 @@ impl SqlWriter { } } -impl std::fmt::Write for SqlWriter { - fn write_str(&mut self, s: &str) -> std::result::Result<(), std::fmt::Error> { +impl Write for SqlWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { write!( self.string, "{}", @@ -96,7 +96,7 @@ impl std::fmt::Write for SqlWriter { #[cfg(test)] #[cfg(feature = "backend-mysql")] mod tests { - use super::*; + use crate::*; #[test] fn inject_parameters_1() { diff --git a/src/query/delete.rs b/src/query/delete.rs index 4eecca3e1..1b6b9655c 100644 --- a/src/query/delete.rs +++ b/src/query/delete.rs @@ -97,7 +97,7 @@ impl DeleteStatement { /// Limit number of updated rows. pub fn limit(&mut self, limit: u64) -> &mut Self { - self.limit = Some(Value::BigUnsigned(Some(limit))); + self.limit = Some(limit.into()); self } diff --git a/src/query/select.rs b/src/query/select.rs index 164676aa9..0a3cf8581 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -1907,7 +1907,7 @@ impl SelectStatement { /// ); /// ``` pub fn limit(&mut self, limit: u64) -> &mut Self { - self.limit = Some(Value::BigUnsigned(Some(limit))); + self.limit = Some(limit.into()); self } @@ -1945,7 +1945,7 @@ impl SelectStatement { /// ); /// ``` pub fn offset(&mut self, offset: u64) -> &mut Self { - self.offset = Some(Value::BigUnsigned(Some(offset))); + self.offset = Some(offset.into()); self } diff --git a/src/query/update.rs b/src/query/update.rs index 52c7a3bf8..3f3821a38 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -220,7 +220,7 @@ impl UpdateStatement { /// Limit number of updated rows. pub fn limit(&mut self, limit: u64) -> &mut Self { - self.limit = Some(Value::BigUnsigned(Some(limit))); + self.limit = Some(limit.into()); self } diff --git a/src/types.rs b/src/types.rs index 6faafbb18..6c2c2cab9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -178,7 +178,7 @@ pub enum JoinOn { } /// Ordering options -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub enum Order { Asc, Desc, diff --git a/src/value.rs b/src/value.rs deleted file mode 100644 index 38cd18f80..000000000 --- a/src/value.rs +++ /dev/null @@ -1,1641 +0,0 @@ -//! Container for all SQL value types. - -#[cfg(feature = "with-json")] -use serde_json::Value as Json; -#[cfg(feature = "with-json")] -use std::str::from_utf8; - -#[cfg(feature = "with-chrono")] -use chrono::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; - -#[cfg(feature = "with-time")] -use time::{OffsetDateTime, PrimitiveDateTime}; - -#[cfg(feature = "with-rust_decimal")] -use rust_decimal::Decimal; - -#[cfg(feature = "with-bigdecimal")] -use bigdecimal::BigDecimal; - -#[cfg(feature = "with-uuid")] -use uuid::Uuid; - -#[cfg(feature = "with-ipnetwork")] -use ipnetwork::IpNetwork; - -#[cfg(feature = "with-ipnetwork")] -use std::net::IpAddr; - -#[cfg(feature = "with-mac_address")] -use mac_address::MacAddress; - -use crate::{BlobSize, ColumnType}; - -/// Value variants -/// -/// We want Value to be exactly 1 pointer sized, so anything larger should be boxed. -#[derive(Clone, Debug, PartialEq)] -pub enum Value { - Bool(Option), - TinyInt(Option), - SmallInt(Option), - Int(Option), - BigInt(Option), - TinyUnsigned(Option), - SmallUnsigned(Option), - Unsigned(Option), - BigUnsigned(Option), - Float(Option), - Double(Option), - String(Option>), - Char(Option), - - #[allow(clippy::box_collection)] - Bytes(Option>>), - - #[cfg(feature = "with-json")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] - Json(Option>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoDate(Option>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoTime(Option>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoDateTime(Option>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoDateTimeUtc(Option>>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoDateTimeLocal(Option>>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoDateTimeWithTimeZone(Option>>), - - #[cfg(feature = "with-time")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] - TimeDate(Option>), - - #[cfg(feature = "with-time")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] - TimeTime(Option>), - - #[cfg(feature = "with-time")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] - TimeDateTime(Option>), - - #[cfg(feature = "with-time")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] - TimeDateTimeWithTimeZone(Option>), - - #[cfg(feature = "with-uuid")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] - Uuid(Option>), - - #[cfg(feature = "with-rust_decimal")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))] - Decimal(Option>), - - #[cfg(feature = "with-bigdecimal")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))] - BigDecimal(Option>), - - #[cfg(feature = "postgres-array")] - #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] - Array(Option>>), - - #[cfg(feature = "with-ipnetwork")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))] - IpNetwork(Option>), - - #[cfg(feature = "with-mac_address")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))] - MacAddress(Option>), -} - -pub trait ValueType: Sized { - fn try_from(v: Value) -> Result; - - fn unwrap(v: Value) -> Self { - Self::try_from(v).unwrap() - } - - fn type_name() -> String; - - fn column_type() -> ColumnType; -} - -#[derive(Debug)] -pub struct ValueTypeErr; - -impl std::error::Error for ValueTypeErr {} - -impl std::fmt::Display for ValueTypeErr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Value type mismatch") - } -} - -#[derive(Clone, Debug, PartialEq)] -pub struct Values(pub Vec); - -#[derive(Clone, Debug, PartialEq)] -pub enum ValueTuple { - One(Value), - Two(Value, Value), - Three(Value, Value, Value), - Four(Value, Value, Value, Value), - Five(Value, Value, Value, Value, Value), - Six(Value, Value, Value, Value, Value, Value), -} - -pub trait IntoValueTuple { - fn into_value_tuple(self) -> ValueTuple; -} - -pub trait FromValueTuple: Sized { - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple; -} - -pub trait Nullable { - fn null() -> Value; -} - -impl Value { - pub fn unwrap(self) -> T - where - T: ValueType, - { - T::unwrap(self) - } -} - -macro_rules! type_to_value { - ( $type: ty, $name: ident, $col_type: expr ) => { - impl From<$type> for Value { - fn from(x: $type) -> Value { - Value::$name(Some(x)) - } - } - - impl Nullable for $type { - fn null() -> Value { - Value::$name(None) - } - } - - impl ValueType for $type { - fn try_from(v: Value) -> Result { - match v { - Value::$name(Some(x)) => Ok(x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!($type).to_owned() - } - - fn column_type() -> ColumnType { - use ColumnType::*; - $col_type - } - } - }; -} - -macro_rules! type_to_box_value { - ( $type: ty, $name: ident, $col_type: expr ) => { - impl From<$type> for Value { - fn from(x: $type) -> Value { - Value::$name(Some(Box::new(x))) - } - } - - impl Nullable for $type { - fn null() -> Value { - Value::$name(None) - } - } - - impl ValueType for $type { - fn try_from(v: Value) -> Result { - match v { - Value::$name(Some(x)) => Ok(*x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!($type).to_owned() - } - - fn column_type() -> ColumnType { - use ColumnType::*; - $col_type - } - } - }; -} - -type_to_value!(bool, Bool, Boolean); -type_to_value!(i8, TinyInt, TinyInteger(None)); -type_to_value!(i16, SmallInt, SmallInteger(None)); -type_to_value!(i32, Int, Integer(None)); -type_to_value!(i64, BigInt, BigInteger(None)); -type_to_value!(u8, TinyUnsigned, TinyUnsigned(None)); -type_to_value!(u16, SmallUnsigned, SmallUnsigned(None)); -type_to_value!(u32, Unsigned, Unsigned(None)); -type_to_value!(u64, BigUnsigned, BigUnsigned(None)); -type_to_value!(f32, Float, Float(None)); -type_to_value!(f64, Double, Double(None)); -type_to_value!(char, Char, Char(None)); - -impl<'a> From<&'a [u8]> for Value { - fn from(x: &'a [u8]) -> Value { - Value::Bytes(Some(Box::>::new(x.into()))) - } -} - -impl<'a> From<&'a str> for Value { - fn from(x: &'a str) -> Value { - let string: String = x.into(); - Value::String(Some(Box::new(string))) - } -} - -impl<'a> Nullable for &'a str { - fn null() -> Value { - Value::String(None) - } -} - -impl From> for Value -where - T: Into + Nullable, -{ - fn from(x: Option) -> Value { - match x { - Some(v) => v.into(), - None => T::null(), - } - } -} - -impl ValueType for Option -where - T: ValueType + Nullable, -{ - fn try_from(v: Value) -> Result { - if v == T::null() { - Ok(None) - } else { - Ok(Some(T::try_from(v)?)) - } - } - - fn type_name() -> String { - format!("Option<{}>", T::type_name()) - } - - fn column_type() -> ColumnType { - T::column_type() - } -} - -type_to_box_value!(Vec, Bytes, Binary(BlobSize::Blob(None))); -type_to_box_value!(String, String, String(None)); - -#[cfg(feature = "with-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] -mod with_json { - use super::*; - - type_to_box_value!(Json, Json, Json); -} - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -mod with_chrono { - use super::*; - use chrono::{Local, Offset, Utc}; - - type_to_box_value!(NaiveDate, ChronoDate, Date); - type_to_box_value!(NaiveTime, ChronoTime, Time(None)); - type_to_box_value!(NaiveDateTime, ChronoDateTime, DateTime(None)); - - impl From> for Value { - fn from(v: DateTime) -> Value { - Value::ChronoDateTimeUtc(Some(Box::new(v))) - } - } - - impl From> for Value { - fn from(v: DateTime) -> Value { - Value::ChronoDateTimeLocal(Some(Box::new(v))) - } - } - - impl From> for Value { - fn from(x: DateTime) -> Value { - let v = DateTime::::from_utc(x.naive_utc(), x.offset().fix()); - Value::ChronoDateTimeWithTimeZone(Some(Box::new(v))) - } - } - - impl Nullable for DateTime { - fn null() -> Value { - Value::ChronoDateTimeUtc(None) - } - } - - impl ValueType for DateTime { - fn try_from(v: Value) -> Result { - match v { - Value::ChronoDateTimeUtc(Some(x)) => Ok(*x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(DateTime).to_owned() - } - - fn column_type() -> ColumnType { - ColumnType::TimestampWithTimeZone(None) - } - } - - impl Nullable for DateTime { - fn null() -> Value { - Value::ChronoDateTimeLocal(None) - } - } - - impl ValueType for DateTime { - fn try_from(v: Value) -> Result { - match v { - Value::ChronoDateTimeLocal(Some(x)) => Ok(*x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(DateTime).to_owned() - } - - fn column_type() -> ColumnType { - ColumnType::TimestampWithTimeZone(None) - } - } - - impl Nullable for DateTime { - fn null() -> Value { - Value::ChronoDateTimeWithTimeZone(None) - } - } - - impl ValueType for DateTime { - fn try_from(v: Value) -> Result { - match v { - Value::ChronoDateTimeWithTimeZone(Some(x)) => Ok(*x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(DateTime).to_owned() - } - - fn column_type() -> ColumnType { - ColumnType::TimestampWithTimeZone(None) - } - } -} - -#[cfg(feature = "with-time")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -pub mod time_format { - use time::format_description::FormatItem; - use time::macros::format_description; - - pub static FORMAT_DATE: &[FormatItem<'static>] = format_description!("[year]-[month]-[day]"); - pub static FORMAT_TIME: &[FormatItem<'static>] = - format_description!("[hour]:[minute]:[second]"); - pub static FORMAT_DATETIME: &[FormatItem<'static>] = - format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); - pub static FORMAT_DATETIME_TZ: &[FormatItem<'static>] = format_description!( - "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]" - ); -} - -#[cfg(feature = "with-time")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -mod with_time { - use super::*; - - type_to_box_value!(time::Date, TimeDate, Date); - type_to_box_value!(time::Time, TimeTime, Time(None)); - type_to_box_value!(PrimitiveDateTime, TimeDateTime, DateTime(None)); - - impl From for Value { - fn from(v: OffsetDateTime) -> Value { - Value::TimeDateTimeWithTimeZone(Some(Box::new(v))) - } - } - - impl Nullable for OffsetDateTime { - fn null() -> Value { - Value::TimeDateTimeWithTimeZone(None) - } - } - - impl ValueType for OffsetDateTime { - fn try_from(v: Value) -> Result { - match v { - Value::TimeDateTimeWithTimeZone(Some(x)) => Ok(*x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(OffsetDateTime).to_owned() - } - - fn column_type() -> ColumnType { - ColumnType::TimestampWithTimeZone(None) - } - } -} - -#[cfg(feature = "with-rust_decimal")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))] -mod with_rust_decimal { - use super::*; - - type_to_box_value!(Decimal, Decimal, Decimal(None)); -} - -#[cfg(feature = "with-bigdecimal")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))] -mod with_bigdecimal { - use super::*; - - type_to_box_value!(BigDecimal, BigDecimal, Decimal(None)); -} - -#[cfg(feature = "with-uuid")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] -mod with_uuid { - use super::*; - - type_to_box_value!(Uuid, Uuid, Uuid); -} - -#[cfg(feature = "with-ipnetwork")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))] -mod with_ipnetwork { - use super::*; - - type_to_box_value!(IpNetwork, IpNetwork, Inet); -} - -#[cfg(feature = "with-mac_address")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))] -mod with_mac_address { - use super::*; - - type_to_box_value!(MacAddress, MacAddress, MacAddr); -} - -#[cfg(feature = "postgres-array")] -#[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] -mod with_array { - use super::*; - - // We only imlement conversion from Vec to Array when T is not u8. - // This is because for u8's case, there is already conversion to Byte defined above. - // TODO When negative trait becomes a stable feature, following code can be much shorter. - pub trait NotU8 {} - - impl NotU8 for bool {} - impl NotU8 for i8 {} - impl NotU8 for i16 {} - impl NotU8 for i32 {} - impl NotU8 for i64 {} - impl NotU8 for u16 {} - impl NotU8 for u32 {} - impl NotU8 for u64 {} - impl NotU8 for f32 {} - impl NotU8 for f64 {} - impl NotU8 for String {} - - #[cfg(feature = "with-json")] - impl NotU8 for Json {} - - #[cfg(feature = "with-chrono")] - impl NotU8 for NaiveDate {} - - #[cfg(feature = "with-chrono")] - impl NotU8 for NaiveTime {} - - #[cfg(feature = "with-chrono")] - impl NotU8 for NaiveDateTime {} - - #[cfg(feature = "with-chrono")] - impl NotU8 for DateTime where Tz: chrono::TimeZone {} - - #[cfg(feature = "with-time")] - impl NotU8 for time::Date {} - - #[cfg(feature = "with-time")] - impl NotU8 for time::Time {} - - #[cfg(feature = "with-time")] - impl NotU8 for PrimitiveDateTime {} - - #[cfg(feature = "with-time")] - impl NotU8 for OffsetDateTime {} - - #[cfg(feature = "with-rust_decimal")] - impl NotU8 for Decimal {} - - #[cfg(feature = "with-bigdecimal")] - impl NotU8 for BigDecimal {} - - #[cfg(feature = "with-uuid")] - impl NotU8 for Uuid {} - - impl From> for Value - where - T: Into + NotU8, - { - fn from(x: Vec) -> Value { - Value::Array(Some(Box::new(x.into_iter().map(|e| e.into()).collect()))) - } - } - - impl Nullable for Vec - where - T: Into + NotU8, - { - fn null() -> Value { - Value::Array(None) - } - } - - impl ValueType for Vec - where - T: NotU8 + ValueType, - { - fn try_from(v: Value) -> Result { - match v { - Value::Array(Some(v)) => Ok(v.into_iter().map(|e| e.unwrap()).collect()), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(Vec).to_owned() - } - - fn column_type() -> ColumnType { - use ColumnType::*; - Array(None) - } - } -} - -#[allow(unused_macros)] -macro_rules! box_to_opt_ref { - ( $v: expr ) => { - match $v { - Some(v) => Some(v.as_ref()), - None => None, - } - }; -} - -#[cfg(feature = "with-json")] -impl Value { - pub fn is_json(&self) -> bool { - matches!(self, Self::Json(_)) - } - - pub fn as_ref_json(&self) -> Option<&Json> { - match self { - Self::Json(v) => box_to_opt_ref!(v), - _ => panic!("not Value::Json"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_date(&self) -> bool { - matches!(self, Self::ChronoDate(_)) - } - - pub fn as_ref_chrono_date(&self) -> Option<&NaiveDate> { - match self { - Self::ChronoDate(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoDate"), - } - } -} - -#[cfg(feature = "with-time")] -impl Value { - pub fn is_time_date(&self) -> bool { - matches!(self, Self::TimeDate(_)) - } - - pub fn as_ref_time_date(&self) -> Option<&time::Date> { - match self { - Self::TimeDate(v) => box_to_opt_ref!(v), - _ => panic!("not Value::TimeDate"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_time(&self) -> bool { - matches!(self, Self::ChronoTime(_)) - } - - pub fn as_ref_chrono_time(&self) -> Option<&NaiveTime> { - match self { - Self::ChronoTime(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoTime"), - } - } -} - -#[cfg(feature = "with-time")] -impl Value { - pub fn is_time_time(&self) -> bool { - matches!(self, Self::TimeTime(_)) - } - - pub fn as_ref_time_time(&self) -> Option<&time::Time> { - match self { - Self::TimeTime(v) => box_to_opt_ref!(v), - _ => panic!("not Value::TimeTime"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_date_time(&self) -> bool { - matches!(self, Self::ChronoDateTime(_)) - } - - pub fn as_ref_chrono_date_time(&self) -> Option<&NaiveDateTime> { - match self { - Self::ChronoDateTime(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoDateTime"), - } - } -} - -#[cfg(feature = "with-time")] -impl Value { - pub fn is_time_date_time(&self) -> bool { - matches!(self, Self::TimeDateTime(_)) - } - - pub fn as_ref_time_date_time(&self) -> Option<&PrimitiveDateTime> { - match self { - Self::TimeDateTime(v) => box_to_opt_ref!(v), - _ => panic!("not Value::TimeDateTime"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_date_time_utc(&self) -> bool { - matches!(self, Self::ChronoDateTimeUtc(_)) - } - - pub fn as_ref_chrono_date_time_utc(&self) -> Option<&DateTime> { - match self { - Self::ChronoDateTimeUtc(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoDateTimeUtc"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_date_time_local(&self) -> bool { - matches!(self, Self::ChronoDateTimeLocal(_)) - } - - pub fn as_ref_chrono_date_time_local(&self) -> Option<&DateTime> { - match self { - Self::ChronoDateTimeLocal(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoDateTimeLocal"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_date_time_with_time_zone(&self) -> bool { - matches!(self, Self::ChronoDateTimeWithTimeZone(_)) - } - - pub fn as_ref_chrono_date_time_with_time_zone(&self) -> Option<&DateTime> { - match self { - Self::ChronoDateTimeWithTimeZone(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoDateTimeWithTimeZone"), - } - } -} - -#[cfg(feature = "with-time")] -impl Value { - pub fn is_time_date_time_with_time_zone(&self) -> bool { - matches!(self, Self::TimeDateTimeWithTimeZone(_)) - } - - pub fn as_ref_time_date_time_with_time_zone(&self) -> Option<&OffsetDateTime> { - match self { - Self::TimeDateTimeWithTimeZone(v) => box_to_opt_ref!(v), - _ => panic!("not Value::TimeDateTimeWithTimeZone"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn chrono_as_naive_utc_in_string(&self) -> Option { - match self { - Self::ChronoDate(v) => v.as_ref().map(|v| v.to_string()), - Self::ChronoTime(v) => v.as_ref().map(|v| v.to_string()), - Self::ChronoDateTime(v) => v.as_ref().map(|v| v.to_string()), - Self::ChronoDateTimeUtc(v) => v.as_ref().map(|v| v.naive_utc().to_string()), - Self::ChronoDateTimeLocal(v) => v.as_ref().map(|v| v.naive_utc().to_string()), - Self::ChronoDateTimeWithTimeZone(v) => v.as_ref().map(|v| v.naive_utc().to_string()), - _ => panic!("not chrono Value"), - } - } -} - -#[cfg(feature = "with-time")] -impl Value { - pub fn time_as_naive_utc_in_string(&self) -> Option { - match self { - Self::TimeDate(v) => v - .as_ref() - .and_then(|v| v.format(time_format::FORMAT_DATE).ok()), - Self::TimeTime(v) => v - .as_ref() - .and_then(|v| v.format(time_format::FORMAT_TIME).ok()), - Self::TimeDateTime(v) => v - .as_ref() - .and_then(|v| v.format(time_format::FORMAT_DATETIME).ok()), - Self::TimeDateTimeWithTimeZone(v) => v.as_ref().and_then(|v| { - v.to_offset(time::macros::offset!(UTC)) - .format(time_format::FORMAT_DATETIME_TZ) - .ok() - }), - _ => panic!("not time Value"), - } - } -} - -#[cfg(feature = "with-rust_decimal")] -impl Value { - pub fn is_decimal(&self) -> bool { - matches!(self, Self::Decimal(_)) - } - - pub fn as_ref_decimal(&self) -> Option<&Decimal> { - match self { - Self::Decimal(v) => box_to_opt_ref!(v), - _ => panic!("not Value::Decimal"), - } - } - - pub fn decimal_to_f64(&self) -> Option { - use rust_decimal::prelude::ToPrimitive; - - self.as_ref_decimal().map(|d| d.to_f64().unwrap()) - } -} - -#[cfg(feature = "with-bigdecimal")] -impl Value { - pub fn is_big_decimal(&self) -> bool { - matches!(self, Self::BigDecimal(_)) - } - - pub fn as_ref_big_decimal(&self) -> Option<&BigDecimal> { - match self { - Self::BigDecimal(v) => box_to_opt_ref!(v), - _ => panic!("not Value::BigDecimal"), - } - } - - pub fn big_decimal_to_f64(&self) -> Option { - use bigdecimal::ToPrimitive; - self.as_ref_big_decimal().map(|d| d.to_f64().unwrap()) - } -} - -#[cfg(feature = "with-uuid")] -impl Value { - pub fn is_uuid(&self) -> bool { - matches!(self, Self::Uuid(_)) - } - pub fn as_ref_uuid(&self) -> Option<&Uuid> { - match self { - Self::Uuid(v) => box_to_opt_ref!(v), - _ => panic!("not Value::Uuid"), - } - } -} - -#[cfg(feature = "postgres-array")] -impl Value { - pub fn is_array(&self) -> bool { - matches!(self, Self::Array(_)) - } - - pub fn as_ref_array(&self) -> Option<&Vec> { - match self { - Self::Array(v) => box_to_opt_ref!(v), - _ => panic!("not Value::Array"), - } - } -} - -#[cfg(feature = "with-ipnetwork")] -impl Value { - pub fn is_ipnetwork(&self) -> bool { - matches!(self, Self::IpNetwork(_)) - } - - pub fn as_ref_ipnetwork(&self) -> Option<&IpNetwork> { - match self { - Self::IpNetwork(v) => box_to_opt_ref!(v), - _ => panic!("not Value::IpNetwork"), - } - } - - pub fn as_ipaddr(&self) -> Option { - match self { - Self::IpNetwork(v) => v.clone().map(|v| v.network()), - _ => panic!("not Value::IpNetwork"), - } - } -} - -#[cfg(feature = "with-mac_address")] -impl Value { - pub fn is_mac_address(&self) -> bool { - matches!(self, Self::MacAddress(_)) - } - - pub fn as_ref_mac_address(&self) -> Option<&MacAddress> { - match self { - Self::MacAddress(v) => box_to_opt_ref!(v), - _ => panic!("not Value::MacAddress"), - } - } -} - -impl IntoIterator for ValueTuple { - type Item = Value; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - match self { - ValueTuple::One(v) => vec![v].into_iter(), - ValueTuple::Two(v, w) => vec![v, w].into_iter(), - ValueTuple::Three(u, v, w) => vec![u, v, w].into_iter(), - ValueTuple::Four(u, v, w, x) => vec![u, v, w, x].into_iter(), - ValueTuple::Five(u, v, w, x, y) => vec![u, v, w, x, y].into_iter(), - ValueTuple::Six(u, v, w, x, y, z) => vec![u, v, w, x, y, z].into_iter(), - } - } -} - -impl IntoValueTuple for ValueTuple { - fn into_value_tuple(self) -> ValueTuple { - self - } -} - -impl IntoValueTuple for V -where - V: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::One(self.into()) - } -} - -impl IntoValueTuple for (V, W) -where - V: Into, - W: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::Two(self.0.into(), self.1.into()) - } -} - -impl IntoValueTuple for (U, V, W) -where - U: Into, - V: Into, - W: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::Three(self.0.into(), self.1.into(), self.2.into()) - } -} - -impl IntoValueTuple for (U, V, W, X) -where - U: Into, - V: Into, - W: Into, - X: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::Four(self.0.into(), self.1.into(), self.2.into(), self.3.into()) - } -} - -impl IntoValueTuple for (U, V, W, X, Y) -where - U: Into, - V: Into, - W: Into, - X: Into, - Y: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::Five( - self.0.into(), - self.1.into(), - self.2.into(), - self.3.into(), - self.4.into(), - ) - } -} - -impl IntoValueTuple for (U, V, W, X, Y, Z) -where - U: Into, - V: Into, - W: Into, - X: Into, - Y: Into, - Z: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::Six( - self.0.into(), - self.1.into(), - self.2.into(), - self.3.into(), - self.4.into(), - self.5.into(), - ) - } -} - -impl FromValueTuple for V -where - V: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::One(u) => u.unwrap(), - _ => panic!("not ValueTuple::One"), - } - } -} - -impl FromValueTuple for (V, W) -where - V: Into + ValueType, - W: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::Two(v, w) => (v.unwrap(), w.unwrap()), - _ => panic!("not ValueTuple::Two"), - } - } -} - -impl FromValueTuple for (U, V, W) -where - U: Into + ValueType, - V: Into + ValueType, - W: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::Three(u, v, w) => (u.unwrap(), v.unwrap(), w.unwrap()), - _ => panic!("not ValueTuple::Three"), - } - } -} - -impl FromValueTuple for (U, V, W, X) -where - U: Into + ValueType, - V: Into + ValueType, - W: Into + ValueType, - X: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::Four(u, v, w, x) => (u.unwrap(), v.unwrap(), w.unwrap(), x.unwrap()), - _ => panic!("not ValueTuple::Four"), - } - } -} - -impl FromValueTuple for (U, V, W, X, Y) -where - U: Into + ValueType, - V: Into + ValueType, - W: Into + ValueType, - X: Into + ValueType, - Y: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::Five(u, v, w, x, y) => { - (u.unwrap(), v.unwrap(), w.unwrap(), x.unwrap(), y.unwrap()) - } - _ => panic!("not ValueTuple::Five"), - } - } -} - -impl FromValueTuple for (U, V, W, X, Y, Z) -where - U: Into + ValueType, - V: Into + ValueType, - W: Into + ValueType, - X: Into + ValueType, - Y: Into + ValueType, - Z: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::Six(u, v, w, x, y, z) => ( - u.unwrap(), - v.unwrap(), - w.unwrap(), - x.unwrap(), - y.unwrap(), - z.unwrap(), - ), - _ => panic!("not ValueTuple::Six"), - } - } -} - -/// Convert value to json value -#[allow(clippy::many_single_char_names)] -#[cfg(feature = "with-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] -pub fn sea_value_to_json_value(value: &Value) -> Json { - use crate::{CommonSqlQueryBuilder, QueryBuilder}; - - match value { - Value::Bool(None) - | Value::TinyInt(None) - | Value::SmallInt(None) - | Value::Int(None) - | Value::BigInt(None) - | Value::TinyUnsigned(None) - | Value::SmallUnsigned(None) - | Value::Unsigned(None) - | Value::BigUnsigned(None) - | Value::Float(None) - | Value::Double(None) - | Value::String(None) - | Value::Char(None) - | Value::Bytes(None) - | Value::Json(None) => Json::Null, - #[cfg(feature = "with-rust_decimal")] - Value::Decimal(None) => Json::Null, - #[cfg(feature = "with-bigdecimal")] - Value::BigDecimal(None) => Json::Null, - #[cfg(feature = "with-uuid")] - Value::Uuid(None) => Json::Null, - #[cfg(feature = "postgres-array")] - Value::Array(None) => Json::Null, - #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(None) => Json::Null, - #[cfg(feature = "with-mac_address")] - Value::MacAddress(None) => Json::Null, - Value::Bool(Some(b)) => Json::Bool(*b), - Value::TinyInt(Some(v)) => (*v).into(), - Value::SmallInt(Some(v)) => (*v).into(), - Value::Int(Some(v)) => (*v).into(), - Value::BigInt(Some(v)) => (*v).into(), - Value::TinyUnsigned(Some(v)) => (*v).into(), - Value::SmallUnsigned(Some(v)) => (*v).into(), - Value::Unsigned(Some(v)) => (*v).into(), - Value::BigUnsigned(Some(v)) => (*v).into(), - Value::Float(Some(v)) => (*v).into(), - Value::Double(Some(v)) => (*v).into(), - Value::String(Some(s)) => Json::String(s.as_ref().clone()), - Value::Char(Some(v)) => Json::String(v.to_string()), - Value::Bytes(Some(s)) => Json::String(from_utf8(s).unwrap().to_string()), - Value::Json(Some(v)) => v.as_ref().clone(), - #[cfg(feature = "with-chrono")] - Value::ChronoDate(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-chrono")] - Value::ChronoTime(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTime(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeWithTimeZone(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeUtc(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeLocal(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-time")] - Value::TimeDate(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-time")] - Value::TimeTime(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-time")] - Value::TimeDateTime(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-time")] - Value::TimeDateTimeWithTimeZone(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-rust_decimal")] - Value::Decimal(Some(v)) => { - use rust_decimal::prelude::ToPrimitive; - v.as_ref().to_f64().unwrap().into() - } - #[cfg(feature = "with-bigdecimal")] - Value::BigDecimal(Some(v)) => { - use bigdecimal::ToPrimitive; - v.as_ref().to_f64().unwrap().into() - } - #[cfg(feature = "with-uuid")] - Value::Uuid(Some(v)) => Json::String(v.to_string()), - #[cfg(feature = "postgres-array")] - Value::Array(Some(v)) => { - Json::Array(v.as_ref().iter().map(sea_value_to_json_value).collect()) - } - #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(Some(_)) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-mac_address")] - Value::MacAddress(Some(_)) => CommonSqlQueryBuilder.value_to_string(value).into(), - } -} - -impl Values { - pub fn iter(&self) -> impl Iterator { - self.0.iter() - } -} - -impl IntoIterator for Values { - type Item = Value; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_value() { - macro_rules! test_value { - ( $type: ty, $val: literal ) => { - let val: $type = $val; - let v: Value = val.into(); - let out: $type = v.unwrap(); - assert_eq!(out, val); - }; - } - - test_value!(u8, 255); - test_value!(u16, 65535); - test_value!(i8, 127); - test_value!(i16, 32767); - test_value!(i32, 1073741824); - test_value!(i64, 8589934592); - } - - #[test] - fn test_option_value() { - macro_rules! test_some_value { - ( $type: ty, $val: literal ) => { - let val: Option<$type> = Some($val); - let v: Value = val.into(); - let out: $type = v.unwrap(); - assert_eq!(out, val.unwrap()); - }; - } - - macro_rules! test_none { - ( $type: ty, $name: ident ) => { - let val: Option<$type> = None; - let v: Value = val.into(); - assert_eq!(v, Value::$name(None)); - }; - } - - test_some_value!(u8, 255); - test_some_value!(u16, 65535); - test_some_value!(i8, 127); - test_some_value!(i16, 32767); - test_some_value!(i32, 1073741824); - test_some_value!(i64, 8589934592); - - test_none!(u8, TinyUnsigned); - test_none!(u16, SmallUnsigned); - test_none!(i8, TinyInt); - test_none!(i16, SmallInt); - test_none!(i32, Int); - test_none!(i64, BigInt); - } - - #[test] - fn test_box_value() { - let val: String = "hello".to_owned(); - let v: Value = val.clone().into(); - let out: String = v.unwrap(); - assert_eq!(out, val); - } - - #[test] - fn test_value_tuple() { - assert_eq!( - 1i32.into_value_tuple(), - ValueTuple::One(Value::Int(Some(1))) - ); - assert_eq!( - "b".into_value_tuple(), - ValueTuple::One(Value::String(Some(Box::new("b".to_owned())))) - ); - assert_eq!( - (1i32, "b").into_value_tuple(), - ValueTuple::Two( - Value::Int(Some(1)), - Value::String(Some(Box::new("b".to_owned()))) - ) - ); - assert_eq!( - (1i32, 2.4f64, "b").into_value_tuple(), - ValueTuple::Three( - Value::Int(Some(1)), - Value::Double(Some(2.4)), - Value::String(Some(Box::new("b".to_owned()))) - ) - ); - assert_eq!( - (1i32, 2.4f64, "b", 123u8).into_value_tuple(), - ValueTuple::Four( - Value::Int(Some(1)), - Value::Double(Some(2.4)), - Value::String(Some(Box::new("b".to_owned()))), - Value::TinyUnsigned(Some(123)) - ) - ); - assert_eq!( - (1i32, 2.4f64, "b", 123u8, 456u16).into_value_tuple(), - ValueTuple::Five( - Value::Int(Some(1)), - Value::Double(Some(2.4)), - Value::String(Some(Box::new("b".to_owned()))), - Value::TinyUnsigned(Some(123)), - Value::SmallUnsigned(Some(456)) - ) - ); - assert_eq!( - (1i32, 2.4f64, "b", 123u8, 456u16, 789u32).into_value_tuple(), - ValueTuple::Six( - Value::Int(Some(1)), - Value::Double(Some(2.4)), - Value::String(Some(Box::new("b".to_owned()))), - Value::TinyUnsigned(Some(123)), - Value::SmallUnsigned(Some(456)), - Value::Unsigned(Some(789)) - ) - ); - } - - #[test] - #[allow(clippy::clone_on_copy)] - fn test_from_value_tuple() { - let mut val = 1i32; - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = "b".to_owned(); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = (1i32, "b".to_owned()); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = (1i32, 2.4f64, "b".to_owned()); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = (1i32, 2.4f64, "b".to_owned(), 123u8); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = (1i32, 2.4f64, "b".to_owned(), 123u8, 456u16); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = (1i32, 2.4f64, "b".to_owned(), 123u8, 456u16, 789u32); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - } - - #[test] - fn test_value_tuple_iter() { - let mut iter = (1i32).into_value_tuple().into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next(), None); - - let mut iter = (1i32, 2.4f64).into_value_tuple().into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next().unwrap(), Value::Double(Some(2.4))); - assert_eq!(iter.next(), None); - - let mut iter = (1i32, 2.4f64, "b").into_value_tuple().into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next().unwrap(), Value::Double(Some(2.4))); - assert_eq!( - iter.next().unwrap(), - Value::String(Some(Box::new("b".to_owned()))) - ); - assert_eq!(iter.next(), None); - - let mut iter = (1i32, 2.4f64, "b", 123u8).into_value_tuple().into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next().unwrap(), Value::Double(Some(2.4))); - assert_eq!( - iter.next().unwrap(), - Value::String(Some(Box::new("b".to_owned()))) - ); - assert_eq!(iter.next().unwrap(), Value::TinyUnsigned(Some(123))); - assert_eq!(iter.next(), None); - - let mut iter = (1i32, 2.4f64, "b", 123u8, 456u16) - .into_value_tuple() - .into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next().unwrap(), Value::Double(Some(2.4))); - assert_eq!( - iter.next().unwrap(), - Value::String(Some(Box::new("b".to_owned()))) - ); - assert_eq!(iter.next().unwrap(), Value::TinyUnsigned(Some(123))); - assert_eq!(iter.next().unwrap(), Value::SmallUnsigned(Some(456))); - assert_eq!(iter.next(), None); - - let mut iter = (1i32, 2.4f64, "b", 123u8, 456u16, 789u32) - .into_value_tuple() - .into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next().unwrap(), Value::Double(Some(2.4))); - assert_eq!( - iter.next().unwrap(), - Value::String(Some(Box::new("b".to_owned()))) - ); - assert_eq!(iter.next().unwrap(), Value::TinyUnsigned(Some(123))); - assert_eq!(iter.next().unwrap(), Value::SmallUnsigned(Some(456))); - assert_eq!(iter.next().unwrap(), Value::Unsigned(Some(789))); - assert_eq!(iter.next(), None); - } - - #[test] - #[cfg(feature = "with-json")] - fn test_json_value() { - let json = serde_json::json! {{ - "a": 25.0, - "b": "hello", - }}; - let value: Value = json.clone().into(); - let out: Json = value.unwrap(); - assert_eq!(out, json); - } - - #[test] - #[cfg(feature = "with-chrono")] - fn test_chrono_value() { - let timestamp = chrono::NaiveDate::from_ymd(2020, 1, 1).and_hms(2, 2, 2); - let value: Value = timestamp.into(); - let out: NaiveDateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-chrono")] - fn test_chrono_utc_value() { - let timestamp = - DateTime::::from_utc(NaiveDate::from_ymd(2022, 1, 2).and_hms(3, 4, 5), Utc); - let value: Value = timestamp.into(); - let out: DateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-chrono")] - fn test_chrono_local_value() { - let timestamp_utc = - DateTime::::from_utc(NaiveDate::from_ymd(2022, 1, 2).and_hms(3, 4, 5), Utc); - let timestamp_local: DateTime = timestamp_utc.into(); - let value: Value = timestamp_local.into(); - let out: DateTime = value.unwrap(); - assert_eq!(out, timestamp_local); - } - - #[test] - #[cfg(feature = "with-chrono")] - fn test_chrono_timezone_value() { - let timestamp = DateTime::parse_from_rfc3339("2020-01-01T02:02:02+08:00").unwrap(); - let value: Value = timestamp.into(); - let out: DateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-chrono")] - fn test_chrono_query() { - use crate::*; - - let string = "2020-01-01T02:02:02+08:00"; - let timestamp = DateTime::parse_from_rfc3339(string).unwrap(); - - let query = Query::select().expr(Expr::val(timestamp)).to_owned(); - - let formatted = "2020-01-01 02:02:02 +08:00"; - - assert_eq!( - query.to_string(MysqlQueryBuilder), - format!("SELECT '{}'", formatted) - ); - assert_eq!( - query.to_string(PostgresQueryBuilder), - format!("SELECT '{}'", formatted) - ); - assert_eq!( - query.to_string(SqliteQueryBuilder), - format!("SELECT '{}'", formatted) - ); - } - - #[test] - #[cfg(feature = "with-time")] - fn test_time_value() { - use time::macros::{date, time}; - let timestamp = date!(2020 - 01 - 01).with_time(time!(2:2:2)); - let value: Value = timestamp.into(); - let out: PrimitiveDateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-time")] - fn test_time_utc_value() { - use time::macros::{date, time}; - let timestamp = date!(2022 - 01 - 02).with_time(time!(3:04:05)).assume_utc(); - let value: Value = timestamp.into(); - let out: OffsetDateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-time")] - fn test_time_local_value() { - use time::macros::{date, offset, time}; - let timestamp_utc = date!(2022 - 01 - 02).with_time(time!(3:04:05)).assume_utc(); - let timestamp_local: OffsetDateTime = timestamp_utc.to_offset(offset!(+3)); - let value: Value = timestamp_local.into(); - let out: OffsetDateTime = value.unwrap(); - assert_eq!(out, timestamp_local); - } - - #[test] - #[cfg(feature = "with-time")] - fn test_time_timezone_value() { - use time::macros::{date, offset, time}; - let timestamp = date!(2022 - 01 - 02) - .with_time(time!(3:04:05)) - .assume_offset(offset!(+8)); - let value: Value = timestamp.into(); - let out: OffsetDateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-time")] - fn test_time_query() { - use crate::*; - use time::macros::datetime; - - let timestamp = datetime!(2020-01-01 02:02:02 +8); - let query = Query::select().expr(Expr::val(timestamp)).to_owned(); - let formatted = "2020-01-01 02:02:02 +0800"; - - assert_eq!( - query.to_string(MysqlQueryBuilder), - format!("SELECT '{}'", formatted) - ); - assert_eq!( - query.to_string(PostgresQueryBuilder), - format!("SELECT '{}'", formatted) - ); - assert_eq!( - query.to_string(SqliteQueryBuilder), - format!("SELECT '{}'", formatted) - ); - } - - #[test] - #[cfg(feature = "with-uuid")] - fn test_uuid_value() { - let uuid = uuid::Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); - let value: Value = uuid.into(); - let out: uuid::Uuid = value.unwrap(); - assert_eq!(out, uuid); - } - - #[test] - #[cfg(feature = "with-rust_decimal")] - fn test_decimal_value() { - use std::str::FromStr; - - let num = "2.02"; - let val = Decimal::from_str(num).unwrap(); - let v: Value = val.into(); - let out: Decimal = v.unwrap(); - assert_eq!(out.to_string(), num); - } - - #[test] - #[cfg(feature = "postgres-array")] - fn test_array_value() { - let array = vec![1, 2, 3, 4, 5]; - let v: Value = array.into(); - let out: Vec = v.unwrap(); - assert_eq!(out, vec![1, 2, 3, 4, 5]); - } -} diff --git a/src/value/mod.rs b/src/value/mod.rs new file mode 100644 index 000000000..19a421f15 --- /dev/null +++ b/src/value/mod.rs @@ -0,0 +1,5 @@ +pub use tuple::*; +pub use types::*; + +mod tuple; +mod types; diff --git a/src/value/tuple.rs b/src/value/tuple.rs new file mode 100644 index 000000000..3620466ea --- /dev/null +++ b/src/value/tuple.rs @@ -0,0 +1,125 @@ +use crate::value::Value; + +#[derive(Debug, Clone)] +pub enum ValueTuple { + One(Value), + Two(Value, Value), + Three(Value, Value, Value), + Four(Value, Value, Value, Value), + Five(Value, Value, Value, Value, Value), + Six(Value, Value, Value, Value, Value, Value), +} + +pub trait IntoValueTuple { + fn into_value_tuple(self) -> ValueTuple; +} + +pub trait FromValueTuple: Sized { + fn from_value_tuple(i: I) -> Self + where + I: IntoValueTuple; +} + +impl IntoIterator for ValueTuple { + type Item = Value; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + match self { + ValueTuple::One(v) => vec![v].into_iter(), + ValueTuple::Two(v, w) => vec![v, w].into_iter(), + ValueTuple::Three(u, v, w) => vec![u, v, w].into_iter(), + ValueTuple::Four(u, v, w, x) => vec![u, v, w, x].into_iter(), + ValueTuple::Five(u, v, w, x, y) => vec![u, v, w, x, y].into_iter(), + ValueTuple::Six(u, v, w, x, y, z) => vec![u, v, w, x, y, z].into_iter(), + } + } +} + +impl IntoValueTuple for ValueTuple { + fn into_value_tuple(self) -> ValueTuple { + self + } +} + +impl IntoValueTuple for V +where + V: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::One(self.into()) + } +} + +impl IntoValueTuple for (V, W) +where + V: Into, + W: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::Two(self.0.into(), self.1.into()) + } +} + +impl IntoValueTuple for (U, V, W) +where + U: Into, + V: Into, + W: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::Three(self.0.into(), self.1.into(), self.2.into()) + } +} + +impl IntoValueTuple for (U, V, W, X) +where + U: Into, + V: Into, + W: Into, + X: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::Four(self.0.into(), self.1.into(), self.2.into(), self.3.into()) + } +} + +impl IntoValueTuple for (U, V, W, X, Y) +where + U: Into, + V: Into, + W: Into, + X: Into, + Y: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::Five( + self.0.into(), + self.1.into(), + self.2.into(), + self.3.into(), + self.4.into(), + ) + } +} + +impl IntoValueTuple for (U, V, W, X, Y, Z) +where + U: Into, + V: Into, + W: Into, + X: Into, + Y: Into, + Z: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::Six( + self.0.into(), + self.1.into(), + self.2.into(), + self.3.into(), + self.4.into(), + self.5.into(), + ) + } +} diff --git a/src/value/types.rs b/src/value/types.rs new file mode 100644 index 000000000..c642ea02e --- /dev/null +++ b/src/value/types.rs @@ -0,0 +1,126 @@ +use std::fmt::Debug; +use std::rc::Rc; + +use crate::ColumnType; + +enum Type { + Bool, + TinyInt, + SmallInt, + Int, + BigInt, + TinyUnsigned, + SmallUnsigned, + Unsigned, + BigUnsigned, + Float, + Double, + String, + Char, + Array(Box), +} + +pub trait ValueTrait: Debug { + fn to_sql_string(&self) -> String; +} + +pub trait ValueType: Sized { + fn type_name() -> String; + fn column_type() -> ColumnType; +} + +#[derive(Debug, Clone)] +pub struct Value(Rc>); +#[derive(Debug, Clone)] +pub struct Values(pub Vec); + +macro_rules! impl_value_trait { + ( $type: ty, $col_type: expr ) => { + impl ValueTrait for $type { + fn to_sql_string(&self) -> String { + self.to_string() + } + } + + impl From<$type> for Value { + fn from(v: $type) -> Value { + Value(Rc::new(Box::new(v))) + } + } + + impl ValueType for $type { + fn type_name() -> String { + stringify!($type).to_owned() + } + + fn column_type() -> ColumnType { + use ColumnType::*; + $col_type + } + } + }; +} + +impl_value_trait!(i8, TinyInteger(None)); +impl_value_trait!(i16, SmallInteger(None)); +impl_value_trait!(i32, Integer(None)); +impl_value_trait!(i64, BigInteger(None)); +impl_value_trait!(u8, TinyUnsigned(None)); +impl_value_trait!(u16, SmallUnsigned(None)); +impl_value_trait!(u32, Unsigned(None)); +impl_value_trait!(u64, BigUnsigned(None)); +impl_value_trait!(f32, Float(None)); +impl_value_trait!(f64, Double(None)); +impl_value_trait!(char, Char(None)); +impl_value_trait!(String, String(None)); + +impl ValueTrait for bool { + fn to_sql_string(&self) -> String { + if *self { + "TRUE".to_owned() + } else { + "FALSE".to_owned() + } + } +} + +impl ValueType for bool { + fn type_name() -> String { + stringify!(bool).to_owned() + } + + fn column_type() -> ColumnType { + ColumnType::Boolean + } +} + +impl ValueTrait for Option { + fn to_sql_string(&self) -> String { + match self { + None => "NULL".to_owned(), + Some(v) => v.to_sql_string(), + } + } +} + +impl From> for Value { + fn from(v: Option) -> Value { + Value(Rc::new(Box::new(v))) + } +} + +impl ValueType for Option { + fn type_name() -> String { + T::type_name() + } + + fn column_type() -> ColumnType { + T::column_type() + } +} + +impl Value { + pub fn to_sql_string(&self) -> String { + self.0.to_sql_string() + } +}