From 1b36dcb7ada321d74ea4125db01f1f88a52dec04 Mon Sep 17 00:00:00 2001 From: asafmahlev Date: Tue, 2 Jan 2024 13:54:14 +0200 Subject: [PATCH] More passing tests (but more errors) --- azure-kusto-data/Cargo.toml | 4 +- azure-kusto-data/examples/query.rs | 50 +++---- azure-kusto-data/src/arrow.rs | 7 +- azure-kusto-data/src/authorization_policy.rs | 6 +- azure-kusto-data/src/error.rs | 3 +- azure-kusto-data/src/lib.rs | 2 + azure-kusto-data/src/models/v2/consts.rs | 1 - azure-kusto-data/src/types/datetime.rs | 68 ++++++++++ azure-kusto-data/src/types/mod.rs | 47 +++---- azure-kusto-data/src/types/timespan.rs | 129 ++++++++++++++----- azure-kusto-data/tests/e2e.rs | 110 +++++++++------- 11 files changed, 282 insertions(+), 145 deletions(-) create mode 100644 azure-kusto-data/src/types/datetime.rs diff --git a/azure-kusto-data/Cargo.toml b/azure-kusto-data/Cargo.toml index 04a6252..0146a6e 100644 --- a/azure-kusto-data/Cargo.toml +++ b/azure-kusto-data/Cargo.toml @@ -40,6 +40,8 @@ time = { version = "0.3", features = [ derive_builder = "0.12" derive_more = { version = "1.0.0-beta.6" , features = ["from", "into", "display", "from_str"] } once_cell = "1" +rust_decimal = { version = "1.33.1", features = ["serde-str"] } +uuid = { version = "1.3.0", features = ["serde"] } [dev-dependencies] arrow = { version = "49.0.0", features = ["prettyprint"] } @@ -49,8 +51,6 @@ tokio = { version = "1.25.0", features = ["macros"] } oauth2 = "4.3.0" criterion = "0.5" clap = { version = "4.1.6", features = ["derive", "env"] } -decimal = "2.1.0" -uuid = { version = "1.3.0", features = ["serde"] } [features] default = ["arrow"] diff --git a/azure-kusto-data/examples/query.rs b/azure-kusto-data/examples/query.rs index fef9446..f943fb4 100644 --- a/azure-kusto-data/examples/query.rs +++ b/azure-kusto-data/examples/query.rs @@ -1,11 +1,11 @@ -use azure_kusto_data::models::V2QueryResult; use azure_kusto_data::prelude::*; -use azure_kusto_data::types::timespan::{KustoDateTime, KustoTimespan}; use clap::Parser; use futures::{pin_mut, TryStreamExt}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::error::Error; +use azure_kusto_data::{KustoBool, KustoDateTime, KustoDecimal, KustoDynamic, KustoGuid, KustoInt, KustoLong, KustoReal, KustoString, KustoTimespan}; +use azure_kusto_data::models::v2::Frame; /// Simple program to greet a person #[derive(Parser, Debug, Clone)] @@ -78,15 +78,15 @@ async fn progressive(args: &Args, client: &KustoClient) -> Result<(), Box println!("header: {:#?}", header), - V2QueryResult::DataTable(table) => println!("table: {:#?}", table), - V2QueryResult::DataSetCompletion(completion) => { + Frame::DataSetHeader(header) => println!("header: {:#?}", header), + Frame::DataTable(table) => println!("table: {:#?}", table), + Frame::DataSetCompletion(completion) => { println!("completion: {:#?}", completion) } - V2QueryResult::TableHeader(header) => println!("header: {:#?}", header), - V2QueryResult::TableFragment(fragment) => println!("fragment: {:#?}", fragment), - V2QueryResult::TableProgress(progress) => println!("progress: {:#?}", progress), - V2QueryResult::TableCompletion(completion) => { + Frame::TableHeader(header) => println!("header: {:#?}", header), + Frame::TableFragment(fragment) => println!("fragment: {:#?}", fragment), + Frame::TableProgress(progress) => println!("progress: {:#?}", progress), + Frame::TableCompletion(completion) => { println!("completion: {:#?}", completion) } } @@ -116,15 +116,15 @@ async fn non_progressive(args: &Args, client: &KustoClient) { for table in &response.results { match table { - V2QueryResult::DataSetHeader(header) => println!("header: {:#?}", header), - V2QueryResult::DataTable(table) => println!("table: {:#?}", table), - V2QueryResult::DataSetCompletion(completion) => { + Frame::DataSetHeader(header) => println!("header: {:#?}", header), + Frame::DataTable(table) => println!("table: {:#?}", table), + Frame::DataSetCompletion(completion) => { println!("completion: {:#?}", completion) } - V2QueryResult::TableHeader(header) => println!("header: {:#?}", header), - V2QueryResult::TableFragment(fragment) => println!("fragment: {:#?}", fragment), - V2QueryResult::TableProgress(progress) => println!("progress: {:#?}", progress), - V2QueryResult::TableCompletion(completion) => { + Frame::TableHeader(header) => println!("header: {:#?}", header), + Frame::TableFragment(fragment) => println!("fragment: {:#?}", fragment), + Frame::TableProgress(progress) => println!("progress: {:#?}", progress), + Frame::TableCompletion(completion) => { println!("completion: {:#?}", completion) } } @@ -137,16 +137,16 @@ async fn non_progressive(args: &Args, client: &KustoClient) { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] struct Item { - vnum: i32, - vdec: String, // optionally, you can use a decimal type here + vnum: KustoInt, + vdec: KustoDecimal, vdate: KustoDateTime, vspan: KustoTimespan, - vobj: Value, - vb: bool, - vreal: f64, - vstr: String, - vlong: i64, - vguid: String, // optionally, you can use a guid type here + vobj: KustoDynamic, + vb: KustoBool, + vreal: KustoReal, + vstr: KustoString, + vlong: KustoLong, + vguid: KustoGuid } async fn to_struct(args: &Args, client: &KustoClient) -> Result<(), Box> { @@ -172,7 +172,7 @@ async fn to_struct(args: &Args, client: &KustoClient) -> Result<(), Box>(); let items = serde_json::from_value::>(Value::Array(rows))?; diff --git a/azure-kusto-data/src/arrow.rs b/azure-kusto-data/src/arrow.rs index 2c47d5a..9b14929 100644 --- a/azure-kusto-data/src/arrow.rs +++ b/azure-kusto-data/src/arrow.rs @@ -12,7 +12,7 @@ use crate::error::Result; use crate::models::v2::{Column, DataTable}; use crate::models::ColumnType; use crate::models::v2::Row::Values; -use crate::types::{KustoDateTime, Timespan}; +use crate::types::{KustoDateTime, KustoTimespan}; fn convert_array_string(values: Vec) -> Result { let strings: Vec> = serde_json::from_value(Value::Array(values))?; @@ -57,9 +57,10 @@ fn convert_array_timespan(values: Vec) -> Result { let durations: Vec> = strings .iter() .map(|s| { - s.parse::() + s.parse::() .ok() - .and_then(|d| i64::try_from(d.0.whole_nanoseconds()).ok()) + .and_then(|d| d.0) + .and_then(|d| i64::try_from(d.whole_nanoseconds()).ok()) }) .collect(); Ok(Arc::new(DurationNanosecondArray::from(durations))) diff --git a/azure-kusto-data/src/authorization_policy.rs b/azure-kusto-data/src/authorization_policy.rs index 124a174..472c8fe 100644 --- a/azure-kusto-data/src/authorization_policy.rs +++ b/azure-kusto-data/src/authorization_policy.rs @@ -73,9 +73,11 @@ impl Policy for AuthorizationPolicy { } }; - let token = cred.get_token(&[".default"]).await?; + let scope = format!("{}/.default", resource); - request.insert_header(AUTHORIZATION, &format!("Bearer {}", dbg!(token.token.secret()))); + let token = cred.get_token(&[&scope]).await?; + + request.insert_header(AUTHORIZATION, &format!("Bearer {}", token.token.secret())); next[0].send(ctx, request, &next[1..]).await } diff --git a/azure-kusto-data/src/error.rs b/azure-kusto-data/src/error.rs index 22a028d..022d017 100644 --- a/azure-kusto-data/src/error.rs +++ b/azure-kusto-data/src/error.rs @@ -4,7 +4,6 @@ use std::fmt::Debug; use thiserror; use crate::models::v2::OneApiError; - /// Error type for kusto operations. #[derive(thiserror::Error, Debug)] pub enum Error { @@ -74,7 +73,7 @@ pub enum ParseError { #[error("Error parsing guid: {0}")] Guid(#[from] uuid::Error), #[error("Error parsing decimal")] - Decimal(()), + Decimal(#[from] rust_decimal::Error), #[error("Error parsing dynamic: {0}")] Dynamic(#[from] serde_json::Error), } diff --git a/azure-kusto-data/src/lib.rs b/azure-kusto-data/src/lib.rs index 77d9d66..60bdd46 100644 --- a/azure-kusto-data/src/lib.rs +++ b/azure-kusto-data/src/lib.rs @@ -19,3 +19,5 @@ pub mod prelude; mod query; pub mod request_options; pub mod types; + +pub use types::*; diff --git a/azure-kusto-data/src/models/v2/consts.rs b/azure-kusto-data/src/models/v2/consts.rs index 59f705f..66b748d 100644 --- a/azure-kusto-data/src/models/v2/consts.rs +++ b/azure-kusto-data/src/models/v2/consts.rs @@ -2,7 +2,6 @@ use serde::{Deserialize, Serialize}; /// Where errors are reported - within the data, at the end of the table, or at the end of the dataset. #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -#[serde(rename_all = "snake_case")] pub enum ErrorReportingPlacement { /// Errors are reported within the data. InData, diff --git a/azure-kusto-data/src/types/datetime.rs b/azure-kusto-data/src/types/datetime.rs new file mode 100644 index 0000000..e1b5203 --- /dev/null +++ b/azure-kusto-data/src/types/datetime.rs @@ -0,0 +1,68 @@ +use std::fmt::Display; +use std::str::FromStr; +use derive_more::{From, Into}; +use serde::{Deserialize, Serialize}; +use time::{OffsetDateTime}; +use time::format_description::well_known::Rfc3339; +use crate::error::{Error, ParseError}; + +/// Datetime for kusto, for serialization and deserialization. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Into, Debug)] +pub struct KustoDateTime(pub Option); + +impl KustoDateTime { + /// Creates a new `KustoDatetime` from a `time::OffsetDateTime`. + pub fn new(value: OffsetDateTime) -> Self { + Self(Some(value)) + } + + /// Creates a null `KustoDatetime`. + pub fn null() -> Self { + Self(None) + } +} + +impl Display for KustoDateTime { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.0 { + Some(v) => write!(f, "KustoDateTime({})", v), + None => write!(f, "null"), + } + } +} + +impl Serialize for KustoDateTime { + fn serialize(&self, serializer: S) -> Result { + match &self.0 { + Some(v) => serializer.serialize_str(&v.format(&Rfc3339).expect("Should never fail")), + None => serializer.serialize_none(), + } + } +} + +impl<'de> Deserialize<'de> for KustoDateTime { + fn deserialize>(deserializer: D) -> Result { + let opt = Option::::deserialize(deserializer)?; + if let Some(s) = opt { + Ok(s.parse::().map_err(|e| serde::de::Error::custom(e.to_string()))?) + } else { + Ok(Self::null()) + } + } +} + +impl FromStr for KustoDateTime { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(Self::new(OffsetDateTime::parse(s, &Rfc3339).map_err( + |e| Error::from(ParseError::DateTime(e)))? + )) + } +} + +impl From for KustoDateTime { + fn from(v: OffsetDateTime) -> Self { + Self::new(v) + } +} diff --git a/azure-kusto-data/src/types/mod.rs b/azure-kusto-data/src/types/mod.rs index 865889a..a391f5b 100644 --- a/azure-kusto-data/src/types/mod.rs +++ b/azure-kusto-data/src/types/mod.rs @@ -4,27 +4,32 @@ use std::convert::Infallible; use derive_more::{Display, From, Into, FromStr}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; -use time::format_description::well_known::Rfc3339; -use time::OffsetDateTime; +use rust_decimal::Decimal; use crate::error::{Error, ParseError}; -pub use crate::types::timespan::Timespan; mod timespan; +mod datetime; macro_rules! kusto_type { ($name:ident, $type:ty, primitive) => { - kusto_type!($name, $type, Copy, PartialEq, PartialOrd, Eq, Ord); + kusto_type!($name, $type, Copy, PartialOrd, Eq, Ord); }; ($name:ident, $type:ty, $($additional:tt),* ) => { #[doc = concat!("Represents a ", stringify!($type), " for kusto, for serialization and deserialization.")] - #[derive(Deserialize, Serialize, Default, Clone, From, Into, Debug, $($additional),*)] + #[derive(Deserialize, Serialize, Default, Clone, From, Into, Debug, PartialEq, $($additional),*)] #[serde(transparent)] pub struct $name(pub Option<$type>); impl $name { + #[doc = concat!("Creates a new ", stringify!($type), " for kusto.")] pub fn new(value: $type) -> Self { Self(Some(value)) } + + #[doc = concat!("Creates a null ", stringify!($type), " for kusto.")] + pub fn null() -> Self { + Self(None) + } } impl Display for $name { @@ -72,39 +77,21 @@ macro_rules! kusto_from_str { kusto_type!(KustoBool, bool, primitive); kusto_type!(KustoInt, i32, primitive); kusto_type!(KustoLong, i64, primitive); -kusto_type!(KustoReal, f64, Copy, PartialEq, PartialOrd); -kusto_type!(KustoDecimal, decimal::d128, Copy); -kusto_type!(KustoString, String, PartialEq, PartialOrd, Eq, Ord); -kusto_type!(KustoDynamic, serde_json::Value, PartialEq, Eq); +kusto_type!(KustoReal, f64, Copy, PartialOrd); +kusto_type!(KustoDecimal, Decimal, primitive); +kusto_type!(KustoString, String, PartialOrd, Eq, Ord); +kusto_type!(KustoDynamic, serde_json::Value, Eq); kusto_type!(KustoGuid, uuid::Uuid, primitive); -kusto_type!(KustoDateTime, OffsetDateTime, primitive); -kusto_type!(KustoTimespan, Timespan, primitive); - +pub use datetime::KustoDateTime; +pub use timespan::KustoTimespan; kusto_from_str!(KustoBool, bool, ParseError::Bool); kusto_from_str!(KustoInt, i32, ParseError::Int); kusto_from_str!(KustoLong, i64, ParseError::Int); kusto_from_str!(KustoReal, f64, ParseError::Float); -kusto_from_str!(KustoDecimal, decimal::d128, ParseError::Decimal); +kusto_from_str!(KustoDecimal, Decimal, ParseError::Decimal); kusto_from_str!(KustoGuid, uuid::Uuid, ParseError::Guid); -impl FromStr for KustoDateTime { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(Self::new(OffsetDateTime::parse(s, &Rfc3339).map_err( - |e| Error::from(ParseError::DateTime(e)))? - )) - } -} - -impl FromStr for KustoTimespan { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(Self::new(s.parse::()?)) - } -} impl FromStr for KustoString { type Err = Infallible; diff --git a/azure-kusto-data/src/types/timespan.rs b/azure-kusto-data/src/types/timespan.rs index 14b1d61..16b62eb 100644 --- a/azure-kusto-data/src/types/timespan.rs +++ b/azure-kusto-data/src/types/timespan.rs @@ -3,14 +3,14 @@ use once_cell::sync::Lazy; use regex::{Captures, Regex}; use std::fmt::{Debug, Display, Formatter}; use std::str::FromStr; +use std::convert::TryInto; +use std::num::TryFromIntError; use derive_more::{From, Into}; -use serde_with::{DeserializeFromStr, SerializeDisplay}; +use serde::{Deserialize, Serialize}; use time::Duration; use crate::error::ParseError; -/// Timespan that serializes to a string in the format `[-][d.]hh:mm:ss[.fffffff]`. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SerializeDisplay, DeserializeFromStr, From, Into)] -pub struct Timespan(pub Duration); + fn parse_regex_segment(captures: &Captures, name: &str) -> i64 { captures @@ -22,8 +22,47 @@ static KUSTO_DURATION_REGEX: Lazy = Lazy::new(|| { Regex::new(r"^(?P-)?((?P\d+)\.)?(?P\d+):(?P\d+):(?P\d+)(\.(?P\d+))?$") .expect("Failed to compile KustoTimespan regex, this should never happen - please report this issue to the Kusto team") }); +/// Timespan that serializes to a string in the format `[-][d.]hh:mm:ss[.fffffff]`. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, From, Into)] +pub struct KustoTimespan(pub Option); + +impl KustoTimespan { + /// Creates a new `KustoTimespan` from a `std::time::Duration`. + fn new(duration: Duration) -> Self { + Self(Some(duration)) + } + + /// Creates a null `KustoTimespan`. + pub fn null() -> Self { + Self(None) + } + + fn format(f: &mut Formatter, d: Duration) -> std::fmt::Result { + let neg = if d.is_negative() { + write!(f, "-")?; + -1 + } else { + 1 + }; + if d.whole_days().abs() > 0 { + write!(f, "{}.", d.whole_days().abs())?; + } + write!( + f, + "{:02}:{:02}:{:02}.{:07}", + neg * (d.whole_hours() - d.whole_days() * 24), + neg * (d.whole_minutes() - d.whole_hours() * 60), + neg * (d.whole_seconds() - d.whole_minutes() * 60), + i128::from(neg) + * (d.whole_nanoseconds() - i128::from(d.whole_seconds()) * 1_000_000_000) + / 100 // Ticks + )?; + Ok(()) + } +} -impl FromStr for Timespan { + +impl FromStr for KustoTimespan { type Err = Error; fn from_str(s: &str) -> Result { @@ -48,39 +87,69 @@ impl FromStr for Timespan { + Duration::seconds(seconds) + Duration::nanoseconds(nanos * 100)); // Ticks - Ok(Self(duration)) + Ok(Self(Some(duration))) } } -impl Display for Timespan { +impl Display for KustoTimespan { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let neg = if self.0.is_negative() { - write!(f, "-")?; - -1 + if let Some(d) = self.0 { + write!(f, "KustoTimespan(")?; + Self::format(f, d)?; + write!(f, ")")?; + + Ok(()) } else { - 1 - }; - if self.0.whole_days().abs() > 0 { - write!(f, "{}.", self.0.whole_days().abs())?; + write!(f, "null") } - write!( - f, - "{:02}:{:02}:{:02}.{:07}", - neg * (self.0.whole_hours() - self.0.whole_days() * 24), - neg * (self.0.whole_minutes() - self.0.whole_hours() * 60), - neg * (self.0.whole_seconds() - self.0.whole_minutes() * 60), - i128::from(neg) - * (self.0.whole_nanoseconds() - i128::from(self.0.whole_seconds()) * 1_000_000_000) - / 100 // Ticks - )?; - Ok(()) } } -impl Debug for Timespan { +impl Debug for KustoTimespan { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{self}") + if let Some(d) = self.0 { + Self::format(f, d)?; + Ok(()) + } else { + write!(f, "null") + } + } +} + +impl Serialize for KustoTimespan { + fn serialize(&self, serializer: S) -> Result { + if let Some(d) = self.0 { + serializer.serialize_str(&format!("{:?}", d)) + } else { + serializer.serialize_none() + } + } +} + +impl<'de> Deserialize<'de> for KustoTimespan { + fn deserialize>(deserializer: D) -> Result { + let opt = Option::::deserialize(deserializer)?; + if let Some(s) = opt { + Ok(s.parse::().map_err(|e| serde::de::Error::custom(e.to_string()))?) + } else { + Ok(Self::null()) + } + } +} + + +impl TryFrom for KustoTimespan { + type Error = TryFromIntError; + + fn try_from(d: std::time::Duration) -> Result { + Ok(Self(Some(Duration::new(d.as_secs().try_into()?, d.subsec_nanos().try_into()?)))) + } +} + +impl From for KustoTimespan { + fn from(d: Duration) -> Self { + Self(Some(d)) } } @@ -103,9 +172,9 @@ mod tests { for (from, to) in refs { assert_eq!( - Timespan::from_str(from) + KustoTimespan::from_str(from) .unwrap_or_else(|_| panic!("Failed to parse duration {}", from)) - .0.whole_nanoseconds(), + .0.unwrap().whole_nanoseconds(), i128::from(to) ); } @@ -123,7 +192,7 @@ mod tests { ]; for duration in refs { - let parsed = Timespan::from_str(duration) + let parsed = KustoTimespan::from_str(duration) .unwrap_or_else(|_| panic!("Failed to parse duration {}", duration)); assert_eq!(format!("{:?}", parsed), duration); } diff --git a/azure-kusto-data/tests/e2e.rs b/azure-kusto-data/tests/e2e.rs index b7f69dc..476b8f4 100644 --- a/azure-kusto-data/tests/e2e.rs +++ b/azure-kusto-data/tests/e2e.rs @@ -1,26 +1,26 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use std::str::FromStr; +use rust_decimal::Decimal; use time::Duration; -use azure_kusto_data::types::timespan::{KustoDateTime, KustoTimespan}; -use decimal::d128; use uuid::Uuid; +use azure_kusto_data::{KustoBool, KustoDateTime, KustoDecimal, KustoDynamic, KustoGuid, KustoInt, KustoLong, KustoReal, KustoString, KustoTimespan}; mod setup; #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] struct Item { - vnum: i32, - vdec: d128, + vnum: KustoInt, + vdec: KustoDecimal, vdate: KustoDateTime, vspan: KustoTimespan, - vobj: Value, - vb: bool, - vreal: f64, - vstr: String, - vlong: i64, - vguid: Uuid, + vobj: KustoDynamic, + vb: KustoBool, + vreal: KustoReal, + vstr: KustoString, + vlong: KustoLong, + vguid: KustoGuid } #[tokio::test] @@ -38,6 +38,7 @@ async fn create_query_delete_table() { 3, decimal(9.9999999999999), datetime(2023-07-08T18:09:05.5678000Z), time(07:43:12.3456000), dynamic({ "moshe": "value3" }), true, 0.99, "zxcv", 9223372036854775805, guid(d8e3575c-a7a0-47b3-8c73-9a7a6aaabc12), +int(null), decimal(null), datetime(null), time(null), dynamic(null), bool(null), real(null),"", long(null), guid(null) ] "#; let response = client @@ -67,58 +68,67 @@ async fn create_query_delete_table() { let expected = vec![ Item { - vnum: 1, - vdec: d128!(2.00000000000001), - vdate: KustoDateTime::from_str("2020-03-04T14:05:01.3109965Z").unwrap(), - vspan: KustoTimespan::from( - Duration::seconds(3600 + 23 * 60 + 45) + Duration::microseconds(678900), - ), + vnum: 1.into(), + vdec: Decimal::from_str_exact("2.00000000000001").unwrap().into(), + vdate: KustoDateTime::from_str("2020-03-04T14:05:01.3109965Z").unwrap().into(), + vspan: (Duration::seconds(3600 + 23 * 60 + 45) + Duration::microseconds(678900)).into(), vobj: Value::Object(serde_json::Map::from_iter(vec![( - "moshe".to_string(), - Value::String("value".to_string()), - )])), - vb: true, - vreal: 0.01, - vstr: "asdf".to_string(), - vlong: 9223372036854775807, - vguid: Uuid::parse_str("74be27de-1e4e-49d9-b579-fe0b331d3642").unwrap(), + "moshe".to_string().into(), + Value::String("value".to_string()).into(), + )])).into(), + vb: true.into(), + vreal: 0.01.into(), + vstr: "asdf".to_string().into(), + vlong: 9223372036854775807.into(), + vguid: Uuid::parse_str("74be27de-1e4e-49d9-b579-fe0b331d3642").unwrap().into(), }, Item { - vnum: 2, - vdec: d128!(5.00000000000005), + vnum: 2.into(), + vdec: Decimal::from_str_exact("5.00000000000005").unwrap().into(), vdate: KustoDateTime::from_str("2022-05-06T16:07:03.1234300Z").unwrap(), - vspan: KustoTimespan::from( - Duration::seconds(4 * 3600 + 56 * 60 + 59) + Duration::microseconds(912000), - ), + vspan: (Duration::seconds(4 * 3600 + 56 * 60 + 59) + Duration::microseconds(912000)).into(), vobj: Value::Object(serde_json::Map::from_iter(vec![( - "moshe".to_string(), - Value::String("value2".to_string()), - )])), - vb: false, - vreal: 0.05, - vstr: "qwerty".to_string(), - vlong: 9223372036854775806, - vguid: Uuid::parse_str("f6e97f76-8b73-45c0-b9ef-f68e8f897713").unwrap(), + "moshe".to_string().into(), + Value::String("value2".to_string()).into(), + )])).into(), + vb: false.into(), + vreal: 0.05.into(), + vstr: "qwerty".to_string().into(), + vlong: 9223372036854775806.into(), + vguid: Uuid::parse_str("f6e97f76-8b73-45c0-b9ef-f68e8f897713").unwrap().into(), }, Item { - vnum: 3, - vdec: d128!(9.9999999999999), + vnum: 3.into(), + vdec: Decimal::from_str_exact("9.9999999999999").unwrap().into(), vdate: KustoDateTime::from_str("2023-07-08T18:09:05.5678000Z").unwrap(), - vspan: KustoTimespan::from( - Duration::seconds(7 * 3600 + 43 * 60 + 12) + Duration::microseconds(345600), - ), + vspan:(Duration::seconds(7 * 3600 + 43 * 60 + 12) + Duration::microseconds(345600)).into(), vobj: Value::Object(serde_json::Map::from_iter(vec![( - "moshe".to_string(), - Value::String("value3".to_string()), - )])), - vb: true, - vreal: 0.99, - vstr: "zxcv".to_string(), - vlong: 9223372036854775805, - vguid: Uuid::parse_str("d8e3575c-a7a0-47b3-8c73-9a7a6aaabc12").unwrap(), + "moshe".to_string().into(), + Value::String("value3".to_string()).into(), + )])).into(), + vb: true.into(), + vreal: 0.99.into(), + vstr: "zxcv".to_string().into(), + vlong: 9223372036854775805.into(), + vguid: Uuid::parse_str("d8e3575c-a7a0-47b3-8c73-9a7a6aaabc12").unwrap().into(), + }, + Item { + vnum: KustoInt::null(), + vdec: KustoDecimal::null(), + vdate: KustoDateTime::null(), + vspan: KustoTimespan::null(), + vobj: KustoDynamic::null(), + vb: KustoBool::null(), + vreal: KustoReal::null(), + vstr: KustoString::new("".into()), + vlong: KustoLong::null(), + vguid: KustoGuid::null(), }, ]; + let rows = rows.into_iter().map(|row| Value::Array(row.into_result().expect("Failed to convert rows"))) + .collect::>(); + let items = serde_json::from_value::>(Value::Array(rows)).expect("Failed to deserialize");