Skip to content

Commit

Permalink
Add support for UUIDs.
Browse files Browse the repository at this point in the history
  • Loading branch information
JenChampagne committed Jul 28, 2024
1 parent 2108cf7 commit cd683a1
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 4 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "odata-params"
version = "0.2.2"
version = "0.3.0"
authors = ["Jenifer Champagne <[email protected]>"]
edition = "2021"
description = """
Expand All @@ -17,3 +17,4 @@ bigdecimal = "0.4"
chrono = "0.4"
chrono-tz = "0.9"
peg = "0.8"
uuid = "1.10"
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ The library supports the following data types in expressions:

- **String**: Enclosed in single quotes `'example'`
- **Number**: Integer and decimal numbers `123`, `45.67`
- **UUID**: UUIDs `da820b39-5ad2-4441-b664-c902dbd377d8`
- **Boolean**: `true`, `false`
- **Date**: ISO 8601 format `YYYY-MM-DD`
- **DateTime**: ISO 8601 format with time `YYYY-MM-DDTHH:MM:SSZ`
- **Time**: ISO 8601 format `HH:MM:SS`
- **Date**: ISO 8601 format `YYYY-MM-DD`
- **DateTime**: ISO 8601 format with time zone `YYYY-MM-DDTHH:MM:SSZ`

## Testing

Expand Down
7 changes: 7 additions & 0 deletions src/filters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod to_query_string;

use bigdecimal::BigDecimal;
use chrono::{DateTime, NaiveDate, NaiveTime, Utc};
use uuid::Uuid;

pub use parse::parse_str;
pub use to_query_string::{to_query_string, write_query_string};
Expand All @@ -13,6 +14,9 @@ pub enum Error {
/// Error during general parsing.
Parsing,

/// Error parsing a uuid.
ParsingUuid,

/// Error parsing a number.
ParsingNumber,

Expand Down Expand Up @@ -119,6 +123,9 @@ pub enum Value {
/// Numeric value.
Number(BigDecimal),

/// Unique ID sometimes referred to as GUIDs.
Uuid(Uuid),

/// Date and time with time zone value.
DateTime(DateTime<Utc>),

Expand Down
12 changes: 11 additions & 1 deletion src/filters/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::{CompareOperator, Error, Expr, Value};
use bigdecimal::BigDecimal;
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveTime, Utc};
use std::str::FromStr;
use uuid::Uuid;

/// Parses an OData v4 `$filter` expression string into an `Expr` AST.
///
Expand Down Expand Up @@ -94,6 +95,7 @@ peg::parser! {
/ datetime_value()
/ date_value()
/ time_value()
/ uuid_value()
/ number_value()
/ v:bool_value() { Ok(v) }
/ v:null_value() { Ok(v) }
Expand All @@ -107,6 +109,14 @@ peg::parser! {
rule number_value() -> Result<Value, Error>
= n:$(['0'..='9']+ ("." ['0'..='9']*)?) { Ok(Value::Number(BigDecimal::from_str(n).map_err(|_| Error::ParsingNumber)?)) }

/// Parses a uuid value.
rule uuid_value() -> Result<Value, Error>
= id:$(hex()*<8> "-" hex()*<4> "-" hex()*<4> "-" hex()*<4> "-" hex()*<12> ) { Ok(Value::Uuid(Uuid::parse_str(id).map_err(|_| Error::ParsingUuid)?)) }

/// Parses a single hexadecimal digit.
rule hex() -> char
= ['0'..='9'|'a'..='f'|'A'..='F']

/// Parses a time value in the format `HH:MM:SS` or `HH:MM`.
rule time() -> Result<NaiveTime, Error>
= hm:$($(['0'..='9']*<1,2>) ":" $(['0'..='9']*<2>)) s:$(":" $(['0'..='9']*<2>))? ms:$("." $(['0'..='9']*<1,9>))? {
Expand Down Expand Up @@ -162,7 +172,7 @@ peg::parser! {
/ "r" { Ok('\r') }
/ "t" { Ok('\t') }
/ r"\" { Ok('\\') }
/ "u" sequence:$(['0'..='9' | 'a'..='f' | 'A'..='F']*<1,8>) {
/ "u" sequence:$(hex()*<1,8>) {
u32::from_str_radix(sequence, 16).ok().and_then(char::from_u32).ok_or(Error::ParsingUnicodeCodePoint)
}

Expand Down
3 changes: 3 additions & 0 deletions src/filters/to_query_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ fn write_value<W: Write>(writer: &mut W, value: &Value) -> fmt::Result {
// Handle numeric values.
Value::Number(n) => write!(writer, "{n}"),

// Handle UUID values.
Value::Uuid(id) => write!(writer, "{id}"),

// Handle datetime values.
Value::DateTime(dt) => write!(writer, "{}", dt.to_rfc3339_opts(Millis, true)),

Expand Down
34 changes: 34 additions & 0 deletions tests/filters_parse_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,40 @@ fn boolean_value() {
);
}

#[test]
fn uuid_value() {
let filter = [
"AuthorId eq d1fdd9d1-8c73-4eb9-a341-3505d4efad78",
"and PackageId ne C0BD12F1-9CAD-4081-977A-04B5AF7EDA0E",
]
.join(" ");
let result = parse_str(filter).expect("valid filter tree");

assert_eq!(
result,
Expr::And(
Expr::Compare(
Expr::Identifier("AuthorId".to_owned()).into(),
Equal,
Expr::Value(Value::Uuid(uuid::uuid!(
"d1fdd9d1-8c73-4eb9-a341-3505d4efad78"
)))
.into()
)
.into(),
Expr::Compare(
Expr::Identifier("PackageId".to_owned()).into(),
NotEqual,
Expr::Value(Value::Uuid(uuid::uuid!(
"c0bd12f1-9cad-4081-977a-04b5af7eda0e"
)))
.into()
)
.into()
)
);
}

#[test]
fn number_value() {
let filter = "price lt 99.99 and code in (11, 27, 42)";
Expand Down

0 comments on commit cd683a1

Please sign in to comment.