Skip to main content

postgrest_parser/ast/
rpc.rs

1use super::{LogicCondition, OrderTerm, SelectItem};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::collections::HashMap;
5
6/// Parameters for calling a PostgreSQL function (RPC - Remote Procedure Call).
7///
8/// PostgREST allows calling database functions via `POST /rpc/function_name` with named arguments.
9/// Function results can be filtered, ordered, and paginated like regular queries.
10///
11/// # SQL Generation
12///
13/// Generates queries like:
14/// ```sql
15/// SELECT * FROM "schema"."function_name"(arg1 := $1, arg2 := $2)
16/// WHERE filter_column = $3
17/// ORDER BY order_column
18/// LIMIT $4 OFFSET $5
19/// ```
20///
21/// # Examples
22///
23/// ```
24/// use postgrest_parser::{RpcParams, parse_filter, parse_order, LogicCondition};
25/// use std::collections::HashMap;
26/// use serde_json::json;
27///
28/// // Simple function call
29/// let mut args = HashMap::new();
30/// args.insert("user_id".to_string(), json!(123));
31///
32/// let params = RpcParams::new("get_user_profile", args);
33/// assert_eq!(params.function_name, "get_user_profile");
34///
35/// // Function call with filtering and pagination
36/// let mut args = HashMap::new();
37/// args.insert("department".to_string(), json!("engineering"));
38///
39/// let filter = parse_filter("active", "eq.true").unwrap();
40/// let order = parse_order("salary.desc").unwrap();
41///
42/// let params = RpcParams::new("list_employees", args)
43///     .with_filters(vec![LogicCondition::Filter(filter)])
44///     .with_order(order)
45///     .with_limit(20)
46///     .with_offset(40);
47///
48/// assert_eq!(params.limit, Some(20));
49/// assert_eq!(params.offset, Some(40));
50/// ```
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
52pub struct RpcParams {
53    /// The name of the function to call
54    pub function_name: String,
55    /// Named arguments to pass to the function
56    pub args: HashMap<String, Value>,
57    /// Optional filters to apply to function results
58    pub filters: Vec<LogicCondition>,
59    /// Optional ordering for function results
60    pub order: Vec<OrderTerm>,
61    /// Optional result limit
62    pub limit: Option<u64>,
63    /// Optional result offset (for pagination)
64    pub offset: Option<u64>,
65    /// Optional columns to select from function results
66    pub returning: Option<Vec<SelectItem>>,
67}
68
69impl RpcParams {
70    /// Creates new RPC parameters with function name and arguments.
71    pub fn new(function_name: impl Into<String>, args: HashMap<String, Value>) -> Self {
72        Self {
73            function_name: function_name.into(),
74            args,
75            filters: Vec::new(),
76            order: Vec::new(),
77            limit: None,
78            offset: None,
79            returning: None,
80        }
81    }
82
83    /// Adds filters to apply to function results.
84    pub fn with_filters(mut self, filters: Vec<LogicCondition>) -> Self {
85        self.filters = filters;
86        self
87    }
88
89    /// Adds ordering to function results.
90    pub fn with_order(mut self, order: Vec<OrderTerm>) -> Self {
91        self.order = order;
92        self
93    }
94
95    /// Sets the maximum number of results to return.
96    pub fn with_limit(mut self, limit: u64) -> Self {
97        self.limit = Some(limit);
98        self
99    }
100
101    /// Sets the offset for pagination.
102    pub fn with_offset(mut self, offset: u64) -> Self {
103        self.offset = Some(offset);
104        self
105    }
106
107    /// Specifies which columns to select from function results.
108    pub fn with_returning(mut self, returning: Vec<SelectItem>) -> Self {
109        self.returning = Some(returning);
110        self
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn test_rpc_params_new() {
120        let mut args = HashMap::new();
121        args.insert("name".to_string(), Value::String("test".to_string()));
122
123        let params = RpcParams::new("my_function", args.clone());
124
125        assert_eq!(params.function_name, "my_function");
126        assert_eq!(params.args, args);
127        assert!(params.filters.is_empty());
128        assert!(params.order.is_empty());
129        assert_eq!(params.limit, None);
130        assert_eq!(params.offset, None);
131        assert_eq!(params.returning, None);
132    }
133
134    #[test]
135    fn test_rpc_params_builder() {
136        let mut args = HashMap::new();
137        args.insert("user_id".to_string(), Value::Number(123.into()));
138
139        let params = RpcParams::new("get_user_posts", args)
140            .with_limit(10)
141            .with_offset(20);
142
143        assert_eq!(params.function_name, "get_user_posts");
144        assert_eq!(params.limit, Some(10));
145        assert_eq!(params.offset, Some(20));
146    }
147
148    #[test]
149    fn test_rpc_params_empty_args() {
150        let params = RpcParams::new("health_check", HashMap::new());
151
152        assert_eq!(params.function_name, "health_check");
153        assert!(params.args.is_empty());
154    }
155}