use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::prelude::*;
#[derive(Deserialize, Serialize)]
#[serde(untagged)]
pub enum Value
{
Integer(i64),
Float(f64),
String(String),
Array(Vec<Value>),
Map(ValueMap),
}
macro_rules! value_into {
($name:tt, $type:ty) => {
impl From<$type> for Value
{
fn from(value: $type) -> Value
{
Value::$name(value.into())
}
}
};
}
value_into!(Integer, i64);
value_into!(Float, f64);
value_into!(String, String);
value_into!(String, &str);
value_into!(Array, Vec<Value>);
value_into!(Map, ValueMap);
pub type ValueMap = HashMap<String, Value>;
#[macro_export]
macro_rules! krql_value_map {
($($k:expr => $v:expr),* $(,)?) => {
{
let value_map: $crate::queries::kdql::ValueMap = core::convert::From::from([$(($k.to_string(), $v.into()),)*]);
value_map
}
};
}
#[derive(Deserialize, Serialize)]
#[serde(untagged)]
pub enum What
{
#[serde(serialize_with = "serialize_all", deserialize_with = "deserialize_all")]
All,
Identifiers(Vec<String>),
}
fn serialize_all<S>(serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str("*")
}
fn deserialize_all<'de, D>(deserializer: D) -> Result<(), D::Error>
where
D: serde::de::Deserializer<'de>,
{
use serde::de::Error;
let s = String::deserialize(deserializer)?;
if s == "*"
{
Ok(())
}
else
{
Err(D::Error::custom("Expected '*'"))
}
}
impl From<Vec<&str>> for What
{
fn from(value: Vec<&str>) -> Self
{
Self::Identifiers(value.into_iter().map(|x| x.to_owned()).collect())
}
}
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum KDQLQuery
{
Create
{
into: String,
documents: Vec<HashMap<String, Value>>,
},
Drop(Vec<String>),
Retrieve
{
what: What,
from: String,
matches: HashMap<String, Value>,
},
}
impl TryFrom<&KDQLQuery> for dbc::Query
{
type Error = Error;
fn try_from(value: &KDQLQuery) -> std::result::Result<Self, Self::Error>
{
let kdql_query = serde_json::to_string(value)?;
Ok(dbc::Query::new(
kdql_query,
Default::default(),
dbc::QueryType::KDQL,
))
}
}
impl TryFrom<KDQLQuery> for dbc::Query
{
type Error = Error;
fn try_from(value: KDQLQuery) -> std::result::Result<Self, Self::Error>
{
(&value).try_into()
}
}
#[cfg(test)]
mod test
{
use super::{KDQLQuery, What};
#[test]
fn test_kdql_create()
{
let query = KDQLQuery::Create {
into: "test".into(),
documents: vec![
krql_value_map!("name" => "a"),
krql_value_map!("name" => "b"),
],
};
assert_eq!(
serde_saphyr::to_string(&query).unwrap(),
r#"create:
into: test
documents:
- name: a
- name: b
"#
);
}
#[test]
fn test_kdql_retrieve_all()
{
let query = KDQLQuery::Retrieve {
what: What::All,
from: "test".into(),
matches: krql_value_map!("name" => "a"),
};
assert_eq!(
serde_saphyr::to_string(&query).unwrap(),
r#"retrieve:
what: "*"
from: test
matches:
name: a
"#
);
}
#[allow(dead_code)]
fn test_kdql_retrieve_age()
{
let query = KDQLQuery::Retrieve {
what: vec!["age"].into(),
from: "test".into(),
matches: krql_value_map!("name" => "a"),
};
assert_eq!(
serde_saphyr::to_string(&query).unwrap(),
r#"retrieve:
what:
- age
from: test
matches:
name: a
"#
);
}
}