This is an automated email from the ASF dual-hosted git repository.

jiayu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sedona-db.git


The following commit(s) were added to refs/heads/main by this push:
     new 1193bb2  Add argument matcher or (#69)
1193bb2 is described below

commit 1193bb29ad88c198508d0e9fcbfddf0fab241940
Author: jp <[email protected]>
AuthorDate: Fri Sep 12 07:24:19 2025 -0700

    Add argument matcher or (#69)
---
 rust/sedona-functions/src/sd_format.rs    |  2 +-
 rust/sedona-functions/src/st_perimeter.rs |  4 +-
 rust/sedona-functions/src/st_transform.rs |  9 ++--
 rust/sedona-schema/src/matchers.rs        | 70 +++++++++++++++++++++++++++----
 4 files changed, 71 insertions(+), 14 deletions(-)

diff --git a/rust/sedona-functions/src/sd_format.rs 
b/rust/sedona-functions/src/sd_format.rs
index aa13875..b1bb33a 100644
--- a/rust/sedona-functions/src/sd_format.rs
+++ b/rust/sedona-functions/src/sd_format.rs
@@ -81,7 +81,7 @@ impl SedonaScalarKernel for SDFormatDefault {
         let matcher = ArgMatcher::new(
             vec![
                 ArgMatcher::is_any(),
-                ArgMatcher::is_optional(ArgMatcher::is_string()),
+                ArgMatcher::optional(ArgMatcher::is_string()),
             ],
             formatted_type,
         );
diff --git a/rust/sedona-functions/src/st_perimeter.rs 
b/rust/sedona-functions/src/st_perimeter.rs
index 993136a..d6e0208 100644
--- a/rust/sedona-functions/src/st_perimeter.rs
+++ b/rust/sedona-functions/src/st_perimeter.rs
@@ -28,8 +28,8 @@ pub fn st_perimeter_udf() -> SedonaScalarUDF {
         ArgMatcher::new(
             vec![
                 ArgMatcher::is_geometry_or_geography(),
-                ArgMatcher::is_optional(ArgMatcher::is_boolean()),
-                ArgMatcher::is_optional(ArgMatcher::is_boolean()),
+                ArgMatcher::optional(ArgMatcher::is_boolean()),
+                ArgMatcher::optional(ArgMatcher::is_boolean()),
             ],
             SedonaType::Arrow(DataType::Float64),
         ),
diff --git a/rust/sedona-functions/src/st_transform.rs 
b/rust/sedona-functions/src/st_transform.rs
index 9030fcb..d028db6 100644
--- a/rust/sedona-functions/src/st_transform.rs
+++ b/rust/sedona-functions/src/st_transform.rs
@@ -32,9 +32,12 @@ pub fn st_transform_udf() -> SedonaScalarUDF {
         ArgMatcher::new(
             vec![
                 ArgMatcher::is_geometry_or_geography(),
-                ArgMatcher::is_string(),
-                ArgMatcher::is_optional(ArgMatcher::is_string()),
-                ArgMatcher::is_optional(ArgMatcher::is_boolean()),
+                ArgMatcher::or(vec![ArgMatcher::is_string(), 
ArgMatcher::is_numeric()]),
+                ArgMatcher::optional(ArgMatcher::or(vec![
+                    ArgMatcher::is_string(),
+                    ArgMatcher::is_numeric(),
+                ])),
+                ArgMatcher::optional(ArgMatcher::is_boolean()),
             ],
             SedonaType::Wkb(Edges::Planar, None),
         ),
diff --git a/rust/sedona-schema/src/matchers.rs 
b/rust/sedona-schema/src/matchers.rs
index 0f95cc5..ba725c8 100644
--- a/rust/sedona-schema/src/matchers.rs
+++ b/rust/sedona-schema/src/matchers.rs
@@ -85,12 +85,12 @@ impl ArgMatcher {
                 if arg == &&SedonaType::Arrow(DataType::Null) || 
matcher.match_type(arg) {
                     arg_iter.next(); // Consume the argument
                     continue; // Move to the next matcher
-                } else if matcher.is_optional() {
+                } else if matcher.optional() {
                     continue; // Skip the optional matcher
                 } else {
                     return false; // Non-optional matcher failed
                 }
-            } else if matcher.is_optional() {
+            } else if matcher.optional() {
                 continue; // Skip remaining optional matchers
             } else {
                 return false; // Non-optional matcher failed with no arguments 
left
@@ -179,11 +179,18 @@ impl ArgMatcher {
     }
 
     /// Matches any argument that is optional
-    pub fn is_optional(
+    pub fn optional(
         matcher: Arc<dyn TypeMatcher + Send + Sync>,
     ) -> Arc<dyn TypeMatcher + Send + Sync> {
         Arc::new(OptionalMatcher { inner: matcher })
     }
+
+    /// Matches if any of the given matchers match
+    pub fn or(
+        matchers: Vec<Arc<dyn TypeMatcher + Send + Sync>>,
+    ) -> Arc<dyn TypeMatcher + Send + Sync> {
+        Arc::new(OrMatcher { matchers })
+    }
 }
 
 /// A TypeMatcher is a predicate on a [SedonaType]
@@ -198,7 +205,7 @@ pub trait TypeMatcher: Debug {
     fn match_type(&self, arg: &SedonaType) -> bool;
 
     /// If this argument is optional, return true
-    fn is_optional(&self) -> bool {
+    fn optional(&self) -> bool {
         false
     }
 
@@ -244,7 +251,7 @@ impl TypeMatcher for OptionalMatcher {
         self.inner.match_type(arg)
     }
 
-    fn is_optional(&self) -> bool {
+    fn optional(&self) -> bool {
         true
     }
 
@@ -253,6 +260,21 @@ impl TypeMatcher for OptionalMatcher {
     }
 }
 
+#[derive(Debug)]
+struct OrMatcher {
+    matchers: Vec<Arc<dyn TypeMatcher + Send + Sync>>,
+}
+
+impl TypeMatcher for OrMatcher {
+    fn match_type(&self, arg: &SedonaType) -> bool {
+        self.matchers.iter().any(|m| m.match_type(arg))
+    }
+
+    fn type_if_null(&self) -> Option<SedonaType> {
+        None
+    }
+}
+
 #[derive(Debug)]
 struct IsGeometryOrGeography {}
 
@@ -446,8 +468,8 @@ mod tests {
         let matcher = ArgMatcher::new(
             vec![
                 ArgMatcher::is_geometry(),
-                ArgMatcher::is_optional(ArgMatcher::is_boolean()),
-                ArgMatcher::is_optional(ArgMatcher::is_numeric()),
+                ArgMatcher::optional(ArgMatcher::is_boolean()),
+                ArgMatcher::optional(ArgMatcher::is_numeric()),
             ],
             SedonaType::Arrow(DataType::Null),
         );
@@ -486,6 +508,38 @@ mod tests {
         ]));
     }
 
+    #[test]
+    fn or_matcher() {
+        let matcher = ArgMatcher::new(
+            vec![
+                ArgMatcher::is_geometry(),
+                ArgMatcher::or(vec![ArgMatcher::is_boolean(), 
ArgMatcher::is_numeric()]),
+            ],
+            SedonaType::Arrow(DataType::Null),
+        );
+
+        // Matches first arg
+        assert!(matcher.matches(&[WKB_GEOMETRY, 
SedonaType::Arrow(DataType::Boolean),]));
+
+        // Matches second arg
+        assert!(matcher.matches(&[WKB_GEOMETRY, 
SedonaType::Arrow(DataType::Int32)]));
+
+        // No match when second arg is incorrect type
+        assert!(!matcher.matches(&[WKB_GEOMETRY, WKB_GEOMETRY]));
+
+        // No match when first arg is incorrect type
+        assert!(!matcher.matches(&[
+            SedonaType::Arrow(DataType::Boolean),
+            SedonaType::Arrow(DataType::Boolean)
+        ]));
+
+        // Return type if null
+        assert_eq!(
+            ArgMatcher::or(vec![ArgMatcher::is_boolean(), 
ArgMatcher::is_numeric()]).type_if_null(),
+            None
+        );
+    }
+
     #[test]
     fn arg_matcher_matches_null() {
         for type_matcher in [
@@ -498,7 +552,7 @@ mod tests {
             ArgMatcher::is_string(),
             ArgMatcher::is_binary(),
             ArgMatcher::is_boolean(),
-            ArgMatcher::is_optional(ArgMatcher::is_numeric()),
+            ArgMatcher::optional(ArgMatcher::is_numeric()),
         ] {
             let matcher = ArgMatcher::new(vec![type_matcher], 
SedonaType::Arrow(DataType::Null));
             assert!(matcher.matches(&[SedonaType::Arrow(DataType::Null)]));

Reply via email to