1
1
use reqwest:: { Client , Error , Method , Response } ;
2
2
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
+
3
15
pub struct Builder {
4
- method : Option < Method > ,
16
+ method : Method ,
5
17
url : String ,
6
18
queries : Vec < ( String , String ) > ,
7
19
headers : Vec < ( String , String ) > ,
8
20
body : Option < String > ,
9
21
}
10
22
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
11
28
impl Builder {
12
- // TODO: Switching schema
13
29
pub fn new ( url : & str ) -> Self {
14
30
Builder {
15
- method : None ,
31
+ method : Method :: GET ,
16
32
url : url. to_string ( ) ,
17
33
queries : Vec :: new ( ) ,
18
34
headers : Vec :: new ( ) ,
19
35
body : None ,
20
36
}
21
37
}
22
38
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, [ .,:()])
23
45
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
+ ) ) ;
27
73
self
28
74
}
29
75
30
76
// TODO: Write-only tables
31
77
// TODO: URL-encoded payload
32
78
// TODO: Allow specifying columns
33
79
pub fn insert ( mut self , body : & str ) -> Self {
34
- self . method = Some ( Method :: POST ) ;
80
+ self . method = Method :: POST ;
35
81
self . headers
36
82
. push ( ( "Prefer" . to_string ( ) , "return=representation" . to_string ( ) ) ) ;
37
83
self . body = Some ( body. to_string ( ) ) ;
@@ -47,41 +93,55 @@ impl Builder {
47
93
// TODO: Allow Prefer: resolution=ignore-duplicates
48
94
// TODO: on_conflict (make UPSERT work on UNIQUE columns)
49
95
pub fn upsert ( mut self , body : & str ) -> Self {
50
- self . method = Some ( Method :: POST ) ;
51
- self . headers
52
- . push ( ( "Prefer" . to_string ( ) ,
53
- "return=representation; resolution=merge-duplicates" . to_string ( ) ) ) ;
96
+ self . method = Method :: POST ;
97
+ self . headers . push ( (
98
+ "Prefer" . to_string ( ) ,
99
+ "return=representation; resolution=merge-duplicates" . to_string ( ) ,
100
+ ) ) ;
54
101
self . body = Some ( body. to_string ( ) ) ;
55
102
self
56
103
}
57
104
58
105
pub fn single_upsert ( mut self , primary_column : & str , key : & str , body : & str ) -> Self {
59
- self . method = Some ( Method :: PUT ) ;
106
+ self . method = Method :: PUT ;
60
107
self . headers
61
108
. push ( ( "Prefer" . to_string ( ) , "return=representation" . to_string ( ) ) ) ;
62
- self . queries . push ( ( primary_column . to_string ( ) ,
63
- format ! ( "eq.{}" , key) ) ) ;
109
+ self . queries
110
+ . push ( ( primary_column . to_string ( ) , format ! ( "eq.{}" , key) ) ) ;
64
111
self . body = Some ( body. to_string ( ) ) ;
65
112
self
66
113
}
67
114
68
115
pub fn update ( mut self , body : & str ) -> Self {
69
- self . method = Some ( Method :: PATCH ) ;
116
+ self . method = Method :: PATCH ;
70
117
self . headers
71
118
. push ( ( "Prefer" . to_string ( ) , "return=representation" . to_string ( ) ) ) ;
72
119
self . body = Some ( body. to_string ( ) ) ;
73
120
self
74
121
}
75
122
76
123
pub fn delete ( mut self ) -> Self {
77
- self . method = Some ( Method :: DELETE ) ;
124
+ self . method = Method :: DELETE ;
78
125
self . headers
79
126
. push ( ( "Prefer" . to_string ( ) , "return=representation" . to_string ( ) ) ) ;
80
127
self
81
128
}
82
129
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
+
83
143
pub async fn execute ( self ) -> Result < Response , Error > {
84
- let mut req = Client :: new ( ) . request ( self . method . unwrap ( ) , & self . url ) ;
144
+ let mut req = Client :: new ( ) . request ( self . method , & self . url ) ;
85
145
for ( k, v) in & self . headers {
86
146
req = req. header ( k, v) ;
87
147
}
0 commit comments