use bytes::Bytes;
use serde::Serialize;
use serde_json::Value as JsonValue;
use super::proto::hyper_service::query_param::{ParameterStyle as ProtoParameterStyle, Parameters};
use super::proto::hyper_service::{QueryParameterArrow, QueryParameterJson};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ParameterStyle {
#[default]
QuestionMark,
DollarNumbered,
Named,
}
impl From<ParameterStyle> for i32 {
fn from(style: ParameterStyle) -> Self {
match style {
ParameterStyle::QuestionMark => ProtoParameterStyle::QuestionMark as i32,
ParameterStyle::DollarNumbered => ProtoParameterStyle::DollarNumbered as i32,
ParameterStyle::Named => ProtoParameterStyle::Named as i32,
}
}
}
impl From<ParameterStyle> for ProtoParameterStyle {
fn from(style: ParameterStyle) -> Self {
match style {
ParameterStyle::QuestionMark => ProtoParameterStyle::QuestionMark,
ParameterStyle::DollarNumbered => ProtoParameterStyle::DollarNumbered,
ParameterStyle::Named => ProtoParameterStyle::Named,
}
}
}
#[derive(Debug, Clone)]
pub enum QueryParameters {
Json(String),
Arrow(Bytes),
}
impl QueryParameters {
pub fn from_json_string(json: impl Into<String>) -> Self {
QueryParameters::Json(json.into())
}
pub fn from_json_value<T: Serialize>(value: &T) -> Result<Self, serde_json::Error> {
let json = serde_json::to_string(value)?;
Ok(QueryParameters::Json(json))
}
pub fn json_positional<T: Serialize + ?Sized>(
values: &[&T],
) -> Result<Self, serde_json::Error> {
let json_values: Vec<JsonValue> = values
.iter()
.map(serde_json::to_value)
.collect::<Result<_, _>>()?;
let json = serde_json::to_string(&json_values)?;
Ok(QueryParameters::Json(json))
}
#[must_use]
pub fn json_named() -> JsonNamedParamsBuilder {
JsonNamedParamsBuilder::new()
}
pub fn from_arrow(data: impl Into<Bytes>) -> Self {
QueryParameters::Arrow(data.into())
}
pub(crate) fn into_proto(self) -> Parameters {
match self {
QueryParameters::Json(json) => {
Parameters::JsonParameters(QueryParameterJson { data: json })
}
QueryParameters::Arrow(data) => {
Parameters::ArrowParameters(QueryParameterArrow { data })
}
}
}
pub fn is_json(&self) -> bool {
matches!(self, QueryParameters::Json(_))
}
pub fn is_arrow(&self) -> bool {
matches!(self, QueryParameters::Arrow(_))
}
}
#[derive(Debug, Default)]
pub struct JsonNamedParamsBuilder {
params: serde_json::Map<String, JsonValue>,
}
impl JsonNamedParamsBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn add<T: Serialize>(
mut self,
name: impl Into<String>,
value: &T,
) -> Result<Self, serde_json::Error> {
let json_value = serde_json::to_value(value)?;
self.params.insert(name.into(), json_value);
Ok(self)
}
#[must_use]
pub fn add_null(mut self, name: impl Into<String>) -> Self {
self.params.insert(name.into(), JsonValue::Null);
self
}
#[must_use]
pub fn build(self) -> QueryParameters {
let json = serde_json::to_string(&JsonValue::Object(self.params))
.expect("serializing Map<String, Value> never fails");
QueryParameters::Json(json)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_json_positional_integers() {
let params = QueryParameters::json_positional(&[&42i64, &100i64, &200i64]).unwrap();
match params {
QueryParameters::Json(json) => {
assert_eq!(json, r"[42,100,200]");
}
QueryParameters::Arrow(_) => panic!("Expected JSON parameters"),
}
}
#[test]
fn test_json_positional_strings() {
let params = QueryParameters::json_positional(&[&"hello", &"world"]).unwrap();
match params {
QueryParameters::Json(json) => {
assert_eq!(json, r#"["hello","world"]"#);
}
QueryParameters::Arrow(_) => panic!("Expected JSON parameters"),
}
}
#[test]
fn test_json_positional_mixed_via_value() {
let values = serde_json::json!([42, "hello", true]);
let params = QueryParameters::from_json_value(&values).unwrap();
match params {
QueryParameters::Json(json) => {
assert_eq!(json, r#"[42,"hello",true]"#);
}
QueryParameters::Arrow(_) => panic!("Expected JSON parameters"),
}
}
#[test]
fn test_json_named() {
let params = QueryParameters::json_named()
.add("id", &42i64)
.unwrap()
.add("name", &"Alice")
.unwrap()
.build();
match params {
QueryParameters::Json(json) => {
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["id"], 42);
assert_eq!(parsed["name"], "Alice");
}
QueryParameters::Arrow(_) => panic!("Expected JSON parameters"),
}
}
#[test]
fn test_json_named_with_null() {
let params = QueryParameters::json_named()
.add("id", &42i64)
.unwrap()
.add_null("optional")
.build();
match params {
QueryParameters::Json(json) => {
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["id"], 42);
assert!(parsed["optional"].is_null());
}
QueryParameters::Arrow(_) => panic!("Expected JSON parameters"),
}
}
#[test]
fn test_from_json_string() {
let params = QueryParameters::from_json_string(r#"{"foo": "bar"}"#);
assert!(params.is_json());
assert!(!params.is_arrow());
}
#[test]
fn test_arrow_params() {
let params = QueryParameters::from_arrow(vec![1, 2, 3, 4]);
assert!(params.is_arrow());
assert!(!params.is_json());
}
#[test]
fn test_parameter_style_conversion() {
assert_eq!(i32::from(ParameterStyle::QuestionMark), 3);
assert_eq!(i32::from(ParameterStyle::DollarNumbered), 1);
assert_eq!(i32::from(ParameterStyle::Named), 2);
}
}