Skip to content

Commit 5edf4e2

Browse files
authored
Merge pull request #8 from supabase/dev/filters
Add basic support for filters
2 parents 8df2dd4 + fb4db9c commit 5edf4e2

File tree

1 file changed

+98
-11
lines changed

1 file changed

+98
-11
lines changed

src/builder.rs

Lines changed: 98 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,147 @@
11
use reqwest::{Client, Error, Method, Response};
22

3+
macro_rules! filter {
4+
( $( $op:ident ),* ) => {
5+
$(
6+
pub fn $op(mut self, column: &str, param: &str) -> Self {
7+
self.queries.push((column.to_string(),
8+
format!("{}.{}", stringify!($op), param)));
9+
self
10+
}
11+
)*
12+
}
13+
}
14+
315
pub struct Builder {
4-
method: Option<Method>,
16+
method: Method,
517
url: String,
618
queries: Vec<(String, String)>,
719
headers: Vec<(String, String)>,
820
body: Option<String>,
921
}
1022

23+
// TODO: Complex filters (not, and, or)
24+
// TODO: Switching schema
25+
// TODO: Exact, planned, estimated count (HEAD verb)
26+
// TODO: Response format
27+
// TODO: Embedded resources
1128
impl Builder {
12-
// TODO: Schema
1329
pub fn new(url: &str) -> Self {
1430
Builder {
15-
method: None,
31+
method: Method::GET,
1632
url: url.to_string(),
1733
queries: Vec::new(),
1834
headers: Vec::new(),
1935
body: None,
2036
}
2137
}
2238

39+
// TODO: Multiple columns
40+
// TODO: Renaming columns
41+
// TODO: Casting columns
42+
// TODO: JSON columns
43+
// TODO: Computed (virtual) columns
44+
// TODO: Investigate character corner cases (Unicode, [ .,:()])
2345
pub fn select(mut self, column: &str) -> Self {
24-
self.method = Some(Method::GET);
25-
let column = column.chars().filter(|c| !c.is_whitespace()).collect();
26-
self.queries.push(("select".to_string(), column));
46+
self.method = Method::GET;
47+
self.queries
48+
.push(("select".to_string(), column.to_string()));
49+
self
50+
}
51+
52+
// TODO: desc/asc
53+
// TODO: nullsfirst/nullslast
54+
// TODO: Multiple columns
55+
// TODO: Computed columns
56+
pub fn order(mut self, column: &str) -> Self {
57+
self.queries.push(("order".to_string(), column.to_string()));
58+
self
59+
}
60+
61+
// TODO: Open-ended range
62+
pub fn limit(mut self, count: usize) -> Self {
63+
self.headers
64+
.push(("Content-Range".to_string(), format!("0-{}", count - 1)));
65+
self
66+
}
67+
68+
pub fn single(mut self) -> Self {
69+
self.headers.push((
70+
"Accept".to_string(),
71+
"application/vnd.pgrst.object+json".to_string(),
72+
));
2773
self
2874
}
2975

3076
// TODO: Write-only tables
31-
// TODO: UPSERT
3277
// TODO: URL-encoded payload
78+
// TODO: Allow specifying columns
3379
pub fn insert(mut self, body: &str) -> Self {
34-
self.method = Some(Method::POST);
80+
self.method = Method::POST;
81+
self.headers
82+
.push(("Prefer".to_string(), "return=representation".to_string()));
83+
self.body = Some(body.to_string());
84+
self
85+
}
86+
87+
pub fn insert_csv(mut self, body: &str) -> Self {
88+
self.headers
89+
.push(("Content-Type".to_string(), "text/csv".to_string()));
90+
self.insert(body)
91+
}
92+
93+
// TODO: Allow Prefer: resolution=ignore-duplicates
94+
// TODO: on_conflict (make UPSERT work on UNIQUE columns)
95+
pub fn upsert(mut self, body: &str) -> Self {
96+
self.method = Method::POST;
97+
self.headers.push((
98+
"Prefer".to_string(),
99+
"return=representation; resolution=merge-duplicates".to_string(),
100+
));
101+
self.body = Some(body.to_string());
102+
self
103+
}
104+
105+
pub fn single_upsert(mut self, primary_column: &str, key: &str, body: &str) -> Self {
106+
self.method = Method::PUT;
35107
self.headers
36108
.push(("Prefer".to_string(), "return=representation".to_string()));
109+
self.queries
110+
.push((primary_column.to_string(), format!("eq.{}", key)));
37111
self.body = Some(body.to_string());
38112
self
39113
}
40114

41115
pub fn update(mut self, body: &str) -> Self {
42-
self.method = Some(Method::PATCH);
116+
self.method = Method::PATCH;
43117
self.headers
44118
.push(("Prefer".to_string(), "return=representation".to_string()));
45119
self.body = Some(body.to_string());
46120
self
47121
}
48122

49123
pub fn delete(mut self) -> Self {
50-
self.method = Some(Method::DELETE);
124+
self.method = Method::DELETE;
51125
self.headers
52126
.push(("Prefer".to_string(), "return=representation".to_string()));
53127
self
54128
}
55129

130+
pub fn in_set(mut self, column: &str, param: &str) -> Self {
131+
self.queries
132+
.push((column.to_string(), format!("in.{}", param)));
133+
self
134+
}
135+
136+
// It's unfortunate that `in` is a keyword, otherwise it'd belong in the
137+
// collection of filters below
138+
filter!(
139+
eq, gt, gte, lt, lte, neq, like, ilike, is, fts, plfts, phfts, wfts, cs, cd, ov, sl, sr,
140+
nxr, nxl, adj, not
141+
);
142+
56143
pub async fn execute(self) -> Result<Response, Error> {
57-
let mut req = Client::new().request(self.method.unwrap(), &self.url);
144+
let mut req = Client::new().request(self.method, &self.url);
58145
for (k, v) in &self.headers {
59146
req = req.header(k, v);
60147
}

0 commit comments

Comments
 (0)