use serde::Serialize;
pub(crate) mod executor;
pub use executor::QueryExecutor;
#[derive(Clone, Debug, Serialize)]
pub struct Query {
#[serde(rename = "query")]
pub(crate) text: String,
#[serde(skip_serializing_if = "Vec::is_empty")] parameters: Vec<QueryParameter>,
}
impl Query {
pub fn with_parameter(
mut self,
name: impl Into<String>,
value: impl Serialize,
) -> azure_core::Result<Self> {
let parameter = QueryParameter {
name: name.into(),
value: serde_json::to_value(value)?,
};
self.parameters.push(parameter);
Ok(self)
}
pub fn with_text(mut self, text: String) -> Self {
self.text = text;
self
}
pub fn append_text(mut self, text: &str) -> Self {
self.text.push_str(text);
self
}
}
impl<T: Into<String>> From<T> for Query {
fn from(value: T) -> Self {
let query = value.into();
Self {
text: query,
parameters: vec![],
}
}
}
#[derive(Clone, Debug, Serialize)]
struct QueryParameter {
name: String,
value: serde_json::Value,
}
#[cfg(test)]
mod tests {
use std::error::Error;
use serde::Serialize;
use crate::Query;
#[test]
pub fn serialize_query_without_parameters() -> Result<(), Box<dyn Error>> {
let query: Query = "SELECT * FROM c".into();
let serialized = serde_json::to_string(&query)?;
assert_eq!(serialized, r#"{"query":"SELECT * FROM c"}"#);
Ok(())
}
#[test]
pub fn serialize_query_with_string_parameters() -> Result<(), Box<dyn Error>> {
let query = Query::from("SELECT * FROM c")
.with_parameter("name1", "value1")?
.with_parameter("name2", "value2")?;
let serialized = serde_json::to_string(&query).unwrap();
assert_eq!(
serialized,
r#"{"query":"SELECT * FROM c","parameters":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}"#
);
Ok(())
}
#[test]
pub fn serialize_query_with_various_parameter_types() -> Result<(), Box<dyn Error>> {
#[derive(Serialize)]
struct ObjectParameter {
name: String,
value: String,
}
let obj_param = ObjectParameter {
name: "foo".into(),
value: "bar".into(),
};
let null_option: Option<&str> = None;
let query = Query::from("SELECT * FROM c")
.with_parameter("string_param", "value1")?
.with_parameter("int_param", 42)?
.with_parameter("float_param", 4.2)?
.with_parameter("bool_param", true)?
.with_parameter("obj_param", obj_param)?
.with_parameter("arr_param", ["a", "b", "c"])?
.with_parameter("null_option", null_option)?
.with_parameter("null_value", ())?;
let serialized = serde_json::to_string(&query).unwrap();
assert_eq!(
serialized,
r#"{"query":"SELECT * FROM c","parameters":[{"name":"string_param","value":"value1"},{"name":"int_param","value":42},{"name":"float_param","value":4.2},{"name":"bool_param","value":true},{"name":"obj_param","value":{"name":"foo","value":"bar"}},{"name":"arr_param","value":["a","b","c"]},{"name":"null_option","value":null},{"name":"null_value","value":null}]}"#
);
Ok(())
}
#[test]
pub fn with_text_replaces_query_text() {
let query = Query::from("SELECT * FROM c").with_text("SELECT c.id FROM c".to_string());
assert_eq!(query.text, "SELECT c.id FROM c");
}
#[test]
pub fn with_text_preserves_parameters() -> Result<(), Box<dyn Error>> {
let query = Query::from("SELECT * FROM c")
.with_parameter("@id", 42)?
.with_text("SELECT c.name FROM c WHERE c.id = @id".to_string());
assert_eq!(query.text, "SELECT c.name FROM c WHERE c.id = @id");
assert_eq!(query.parameters.len(), 1);
assert_eq!(query.parameters[0].name, "@id");
Ok(())
}
#[test]
pub fn append_text_adds_to_existing_text() {
let query = Query::from("SELECT * FROM c").append_text(" WHERE c.id = @id");
assert_eq!(query.text, "SELECT * FROM c WHERE c.id = @id");
}
#[test]
pub fn append_text_preserves_parameters() -> Result<(), Box<dyn Error>> {
let query = Query::from("SELECT * FROM c")
.with_parameter("@id", 42)?
.append_text(" WHERE c.id = @id");
assert_eq!(query.text, "SELECT * FROM c WHERE c.id = @id");
assert_eq!(query.parameters.len(), 1);
assert_eq!(query.parameters[0].name, "@id");
Ok(())
}
#[test]
pub fn method_chaining_works_with_new_methods() -> Result<(), Box<dyn Error>> {
let query = Query::from("SELECT * FROM c")
.append_text(" WHERE c.time >= @low_time")
.with_parameter("@low_time", "2023-01-01")?
.append_text(" AND c.time <= @high_time")
.with_parameter("@high_time", "2023-12-31")?;
assert_eq!(
query.text,
"SELECT * FROM c WHERE c.time >= @low_time AND c.time <= @high_time"
);
assert_eq!(query.parameters.len(), 2);
Ok(())
}
}