Skip to content

Commit 1080adc

Browse files
authored
Finalize API for v1.0.0 (#18)
BREAKING CHANGE: finalize API for v1.0.0
1 parent f026de3 commit 1080adc

File tree

3 files changed

+504
-217
lines changed

3 files changed

+504
-217
lines changed

src/builder.rs

Lines changed: 52 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,76 +3,65 @@ use reqwest::{
33
Client, Error, Method, Response,
44
};
55

6-
#[derive(Default)]
76
pub struct Builder {
87
method: Method,
98
url: String,
109
schema: Option<String>,
10+
// Need this to allow access from `filter.rs`
1111
pub(crate) queries: Vec<(String, String)>,
1212
headers: HeaderMap,
1313
body: Option<String>,
1414
is_rpc: bool,
1515
}
1616

17-
// TODO: Complex filters (not, and, or)
18-
// TODO: Exact, planned, estimated count (HEAD verb)
19-
// TODO: Response format
20-
// TODO: Resource embedding (embedded filters, etc.)
21-
// TODO: Content-Type (text/csv, etc.)
22-
// TODO: Reject update/delete w/o filters
17+
// TODO: Test Unicode support
2318
impl Builder {
24-
pub fn new<S>(url: S, schema: Option<String>) -> Self
19+
pub fn new<T>(url: T, schema: Option<String>) -> Self
2520
where
26-
S: Into<String>,
21+
T: Into<String>,
2722
{
2823
let mut builder = Builder {
2924
method: Method::GET,
3025
url: url.into(),
3126
schema,
27+
queries: Vec::new(),
3228
headers: HeaderMap::new(),
33-
..Default::default()
29+
body: None,
30+
is_rpc: false,
3431
};
3532
builder
3633
.headers
3734
.insert("Accept", HeaderValue::from_static("application/json"));
3835
builder
3936
}
4037

41-
pub fn auth<S>(mut self, token: S) -> Self
38+
pub fn auth<T>(mut self, token: T) -> Self
4239
where
43-
S: Into<String>,
40+
T: Into<String>,
4441
{
45-
self.headers.append(
42+
self.headers.insert(
4643
"Authorization",
4744
HeaderValue::from_str(&format!("Bearer {}", token.into())).unwrap(),
4845
);
4946
self
5047
}
5148

52-
// TODO: Multiple columns
53-
// TODO: Renaming columns
54-
// TODO: Casting columns
55-
// TODO: JSON columns
56-
// TODO: Computed (virtual) columns
57-
// TODO: Investigate character corner cases (Unicode, [ .,:()])
58-
pub fn select<S>(mut self, column: S) -> Self
49+
// TODO: Renaming, casting, & JSON column examples
50+
// TODO: Resource embedding example
51+
pub fn select<T>(mut self, columns: T) -> Self
5952
where
60-
S: Into<String>,
53+
T: Into<String>,
6154
{
6255
self.method = Method::GET;
63-
self.queries.push(("select".to_string(), column.into()));
56+
self.queries.push(("select".to_string(), columns.into()));
6457
self
6558
}
6659

67-
// TODO: desc/asc
68-
// TODO: nullsfirst/nullslast
69-
// TODO: Multiple columns
70-
// TODO: Computed columns
71-
pub fn order<S>(mut self, column: S) -> Self
60+
pub fn order<T>(mut self, columns: T) -> Self
7261
where
73-
S: Into<String>,
62+
T: Into<String>,
7463
{
75-
self.queries.push(("order".to_string(), column.into()));
64+
self.queries.push(("order".to_string(), columns.into()));
7665
self
7766
}
7867

@@ -96,6 +85,31 @@ impl Builder {
9685
self
9786
}
9887

88+
fn count(mut self, method: &str) -> Self {
89+
self.headers
90+
.insert("Range-Unit", HeaderValue::from_static("items"));
91+
// Value is irrelevant, we just want the size
92+
self.headers
93+
.insert("Range", HeaderValue::from_static("0-0"));
94+
self.headers.insert(
95+
"Prefer",
96+
HeaderValue::from_str(&format!("count={}", method)).unwrap(),
97+
);
98+
self
99+
}
100+
101+
pub fn exact_count(self) -> Self {
102+
self.count("exact")
103+
}
104+
105+
pub fn planned_count(self) -> Self {
106+
self.count("planned")
107+
}
108+
109+
pub fn estimated_count(self) -> Self {
110+
self.count("estimated")
111+
}
112+
99113
pub fn single(mut self) -> Self {
100114
self.headers.insert(
101115
"Accept",
@@ -104,12 +118,9 @@ impl Builder {
104118
self
105119
}
106120

107-
// TODO: Write-only tables
108-
// TODO: URL-encoded payload
109-
// TODO: Allow specifying columns
110-
pub fn insert<S>(mut self, body: S) -> Self
121+
pub fn insert<T>(mut self, body: T) -> Self
111122
where
112-
S: Into<String>,
123+
T: Into<String>,
113124
{
114125
self.method = Method::POST;
115126
self.headers
@@ -118,11 +129,9 @@ impl Builder {
118129
self
119130
}
120131

121-
// TODO: Allow Prefer: resolution=ignore-duplicates
122-
// TODO: on_conflict (make UPSERT work on UNIQUE columns)
123-
pub fn upsert<S>(mut self, body: S) -> Self
132+
pub fn upsert<T>(mut self, body: T) -> Self
124133
where
125-
S: Into<String>,
134+
T: Into<String>,
126135
{
127136
self.method = Method::POST;
128137
self.headers.insert(
@@ -133,24 +142,9 @@ impl Builder {
133142
self
134143
}
135144

136-
pub fn single_upsert<S, T, U>(mut self, primary_column: S, key: T, body: U) -> Self
145+
pub fn update<T>(mut self, body: T) -> Self
137146
where
138-
S: Into<String>,
139147
T: Into<String>,
140-
U: Into<String>,
141-
{
142-
self.method = Method::PUT;
143-
self.headers
144-
.insert("Prefer", HeaderValue::from_static("return=representation"));
145-
self.queries
146-
.push((primary_column.into(), format!("eq.{}", key.into())));
147-
self.body = Some(body.into());
148-
self
149-
}
150-
151-
pub fn update<S>(mut self, body: S) -> Self
152-
where
153-
S: Into<String>,
154148
{
155149
self.method = Method::PATCH;
156150
self.headers
@@ -166,9 +160,9 @@ impl Builder {
166160
self
167161
}
168162

169-
pub fn rpc<S>(mut self, params: S) -> Self
163+
pub fn rpc<T>(mut self, params: T) -> Self
170164
where
171-
S: Into<String>,
165+
T: Into<String>,
172166
{
173167
self.method = Method::POST;
174168
self.body = Some(params.into());
@@ -185,7 +179,7 @@ impl Builder {
185179
"Content-Profile"
186180
};
187181
self.headers
188-
.append(key, HeaderValue::from_str(&schema).unwrap());
182+
.insert(key, HeaderValue::from_str(&schema).unwrap());
189183
}
190184
if self.method != Method::GET && self.method != Method::HEAD {
191185
self.headers
@@ -205,7 +199,7 @@ mod tests {
205199
use super::*;
206200

207201
const TABLE_URL: &str = "http://localhost:3000/table";
208-
const RPC_URL: &str = "http://localhost/rpc";
202+
const RPC_URL: &str = "http://localhost:3000/rpc";
209203

210204
#[test]
211205
fn only_accept_json() {
@@ -284,15 +278,6 @@ mod tests {
284278
);
285279
}
286280

287-
#[test]
288-
fn single_upsert_assert_prefer_header() {
289-
let builder = Builder::new(TABLE_URL, None).single_upsert("ignored", "ignored", "ignored");
290-
assert_eq!(
291-
builder.headers.get("Prefer").unwrap(),
292-
HeaderValue::from_static("return=representation")
293-
);
294-
}
295-
296281
#[test]
297282
fn not_rpc_should_not_have_flag() {
298283
let builder = Builder::new(TABLE_URL, None).select("ignored");

0 commit comments

Comments
 (0)