postgrest-parser 0.2.0

PostgREST URL-to-SQL parser for Rust and WASM
Documentation
use super::Field;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FilterOperator {
    Eq,
    Neq,
    Gt,
    Gte,
    Lt,
    Lte,
    Like,
    Ilike,
    Match,
    Imatch,
    In,
    Is,
    Fts,
    Plfts,
    Phfts,
    Wfts,
    Cs,
    Cd,
    Ov,
    Sl,
    Sr,
    Nxl,
    Nxr,
    Adj,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Quantifier {
    Any,
    All,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum FilterValue {
    Single(String),
    List(Vec<String>),
}

impl FilterValue {
    pub fn as_str(&self) -> Option<&str> {
        match self {
            FilterValue::Single(s) => Some(s.as_str()),
            FilterValue::List(_) => None,
        }
    }

    pub fn as_list(&self) -> Option<&[String]> {
        match self {
            FilterValue::Single(_) => None,
            FilterValue::List(list) => Some(list.as_slice()),
        }
    }

    pub fn to_json(&self) -> serde_json::Value {
        match self {
            FilterValue::Single(s) => serde_json::Value::String(s.clone()),
            FilterValue::List(list) => serde_json::Value::Array(
                list.iter()
                    .map(|s| {
                        if let Ok(i) = s.parse::<i64>() {
                            serde_json::Value::Number(i.into())
                        } else if let Ok(f) = s.parse::<f64>() {
                            if let Some(num) = serde_json::Number::from_f64(f) {
                                serde_json::Value::Number(num)
                            } else {
                                serde_json::Value::String(s.clone())
                            }
                        } else {
                            serde_json::Value::String(s.clone())
                        }
                    })
                    .collect(),
            ),
        }
    }
}

impl From<&str> for FilterValue {
    fn from(s: &str) -> Self {
        FilterValue::Single(s.to_string())
    }
}

impl From<String> for FilterValue {
    fn from(s: String) -> Self {
        FilterValue::Single(s)
    }
}

impl From<Vec<String>> for FilterValue {
    fn from(v: Vec<String>) -> Self {
        FilterValue::List(v)
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Filter {
    pub field: Field,
    pub operator: FilterOperator,
    pub value: FilterValue,
    pub quantifier: Option<Quantifier>,
    pub language: Option<String>,
    pub negated: bool,
}

impl Filter {
    pub fn new(field: Field, operator: FilterOperator, value: FilterValue) -> Self {
        Self {
            field,
            operator,
            value,
            quantifier: None,
            language: None,
            negated: false,
        }
    }

    pub fn with_quantifier(mut self, quantifier: Quantifier) -> Self {
        self.quantifier = Some(quantifier);
        self
    }

    pub fn with_language(mut self, language: impl Into<String>) -> Self {
        self.language = Some(language.into());
        self
    }

    pub fn negated(mut self) -> Self {
        self.negated = true;
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_filter_new() {
        let field = Field::new("id");
        let filter = Filter::new(
            field,
            FilterOperator::Eq,
            FilterValue::Single("1".to_string()),
        );

        assert_eq!(filter.operator, FilterOperator::Eq);
        assert!(!filter.negated);
        assert!(filter.quantifier.is_none());
    }

    #[test]
    fn test_filter_with_quantifier() {
        let field = Field::new("status");
        let filter = Filter::new(
            field,
            FilterOperator::Eq,
            FilterValue::List(vec!["active".to_string(), "pending".to_string()]),
        )
        .with_quantifier(Quantifier::Any);

        assert_eq!(filter.quantifier, Some(Quantifier::Any));
    }

    #[test]
    fn test_filter_negated() {
        let field = Field::new("status");
        let filter = Filter::new(
            field,
            FilterOperator::Eq,
            FilterValue::Single("deleted".to_string()),
        )
        .negated();

        assert!(filter.negated);
    }

    #[test]
    fn test_filter_value_as_str() {
        let value = FilterValue::Single("test".to_string());
        assert_eq!(value.as_str(), Some("test"));
    }

    #[test]
    fn test_filter_value_as_list() {
        let value = FilterValue::List(vec!["a".to_string(), "b".to_string()]);
        let expected: Vec<String> = vec!["a".to_string(), "b".to_string()];
        assert_eq!(value.as_list(), Some(expected.as_slice()));
    }

    #[test]
    fn test_filter_value_to_json() {
        let value = FilterValue::Single("test".to_string());
        let json = value.to_json();
        assert_eq!(json, serde_json::Value::String("test".to_string()));
    }

    #[test]
    fn test_filter_value_list_to_json() {
        let value = FilterValue::List(vec!["1".to_string(), "2".to_string(), "3".to_string()]);
        let json = value.to_json();
        match json {
            serde_json::Value::Array(arr) => {
                assert_eq!(arr.len(), 3);
                assert_eq!(arr[0], serde_json::Value::Number(1.into()));
            }
            _ => panic!("Expected array"),
        }
    }
}