Skip to content

Commit 5a2f531

Browse files
ramon-bernardoudoprog
authored andcommitted
modules: Expand http module
1 parent b02e5e2 commit 5a2f531

File tree

1 file changed

+276
-9
lines changed

1 file changed

+276
-9
lines changed

crates/rune-modules/src/http.rs

+276-9
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
5151
use rune::alloc::fmt::TryWrite;
5252
use rune::runtime::{Bytes, Formatter, Ref, VmResult};
53-
use rune::{Any, ContextError, Module, Value};
53+
use rune::{docstring, Any, ContextError, Module, Value};
5454

5555
/// A simple HTTP module for Rune.
5656
///
@@ -69,26 +69,56 @@ pub fn module(_stdio: bool) -> Result<Module, ContextError> {
6969
module.ty::<Response>()?;
7070
module.ty::<RequestBuilder>()?;
7171
module.ty::<StatusCode>()?;
72+
module.ty::<Version>()?;
7273
module.ty::<Error>()?;
7374

74-
module.function_meta(Client::new)?;
7575
module.function_meta(get)?;
7676

77+
module.function_meta(Client::new)?;
7778
module.function_meta(Client::get)?;
7879
module.function_meta(Client::post)?;
80+
module.function_meta(Client::put)?;
81+
module.function_meta(Client::delete)?;
82+
module.function_meta(Client::head)?;
7983

8084
module.function_meta(Response::text)?;
8185
module.function_meta(Response::json)?;
8286
module.function_meta(Response::bytes)?;
8387
module.function_meta(Response::status)?;
88+
module.function_meta(Response::version)?;
89+
module.function_meta(Response::content_length)?;
8490

8591
module.function_meta(RequestBuilder::send)?;
8692
module.function_meta(RequestBuilder::header)?;
87-
module.function_meta(RequestBuilder::body_bytes)?;
93+
module.function_meta(RequestBuilder::basic_auth)?;
94+
module.function_meta(RequestBuilder::bearer_auth)?;
8895
module.function_meta(RequestBuilder::fetch_mode_no_cors)?;
96+
module.function_meta(RequestBuilder::body_bytes)?;
8997

90-
module.function_meta(Error::string_display)?;
9198
module.function_meta(StatusCode::string_display)?;
99+
module.function_meta(StatusCode::as_u16)?;
100+
module.function_meta(StatusCode::as_str)?;
101+
module.function_meta(StatusCode::canonical_reason)?;
102+
module.function_meta(StatusCode::is_informational)?;
103+
module.function_meta(StatusCode::is_success)?;
104+
module.function_meta(StatusCode::is_redirection)?;
105+
module.function_meta(StatusCode::is_client_error)?;
106+
module.function_meta(StatusCode::is_server_error)?;
107+
108+
module.implement_trait::<StatusCode>(rune::item!(::std::cmp::PartialEq))?;
109+
module.implement_trait::<StatusCode>(rune::item!(::std::cmp::Eq))?;
110+
module.implement_trait::<StatusCode>(rune::item!(::std::cmp::PartialOrd))?;
111+
module.implement_trait::<StatusCode>(rune::item!(::std::cmp::Ord))?;
112+
113+
module.function_meta(Version::string_display)?;
114+
115+
module.implement_trait::<Version>(rune::item!(::std::cmp::PartialEq))?;
116+
module.implement_trait::<Version>(rune::item!(::std::cmp::Eq))?;
117+
module.implement_trait::<Version>(rune::item!(::std::cmp::PartialOrd))?;
118+
module.implement_trait::<Version>(rune::item!(::std::cmp::Ord))?;
119+
120+
module.function_meta(Error::string_display)?;
121+
92122
Ok(module)
93123
}
94124

@@ -146,6 +176,16 @@ impl Response {
146176
}
147177

148178
/// Get the response as a Rune value decoded from JSON.
179+
///
180+
/// ```rune,no_run
181+
/// let client = http::Client::new();
182+
///
183+
/// let response = client.get("http://example.com")
184+
/// .send()
185+
/// .await?;
186+
///
187+
/// let response = response.json().await?;
188+
/// ```
149189
#[rune::function]
150190
async fn json(self) -> Result<Value, Error> {
151191
let text = self.response.json().await?;
@@ -171,15 +211,34 @@ impl Response {
171211
}
172212

173213
/// Get the status code of the response.
174-
#[rune::function]
214+
#[rune::function(instance)]
175215
fn status(&self) -> StatusCode {
176216
let inner = self.response.status();
177217
StatusCode { inner }
178218
}
219+
220+
/// Get the version of the response.
221+
#[rune::function(instance)]
222+
fn version(&self) -> Version {
223+
let inner = self.response.version();
224+
Version { inner }
225+
}
226+
227+
/// Get the content-length of this response, if known.
228+
///
229+
/// Reasons it may not be known:
230+
///
231+
/// - The server didn't send a `content-length` header.
232+
/// - The response is compressed and automatically decoded (thus changing
233+
/// the actual decoded length).
234+
#[rune::function(instance)]
235+
fn content_length(&self) -> Option<u64> {
236+
self.response.content_length()
237+
}
179238
}
180239

181240
/// An HTTP status code.
182-
#[derive(Debug, Any)]
241+
#[derive(Debug, Any, PartialEq, Eq, PartialOrd, Ord)]
183242
#[rune(item = ::http)]
184243
pub struct StatusCode {
185244
inner: reqwest::StatusCode,
@@ -190,6 +249,77 @@ impl StatusCode {
190249
fn string_display(&self, f: &mut Formatter) -> VmResult<()> {
191250
rune::vm_write!(f, "{}", self.inner)
192251
}
252+
253+
/// Returns the `Integer` corresponding to this `StatusCode`.
254+
#[rune::function(instance)]
255+
#[inline]
256+
fn as_u16(&self) -> u16 {
257+
self.inner.as_u16()
258+
}
259+
260+
/// Returns a String representation of the `StatusCode`.
261+
#[rune::function(instance)]
262+
#[inline]
263+
fn as_str(&self) -> String {
264+
self.inner.as_str().to_owned()
265+
}
266+
267+
/// Get the standardised `reason-phrase` for this status code.
268+
#[inline]
269+
#[rune::function(instance)]
270+
fn canonical_reason(&self) -> Option<&'static str> {
271+
self.inner.canonical_reason()
272+
}
273+
274+
/// Check if status is within 100-199.
275+
#[inline]
276+
#[rune::function(instance)]
277+
fn is_informational(&self) -> bool {
278+
self.inner.is_informational()
279+
}
280+
281+
/// Check if status is within 200-299.
282+
#[inline]
283+
#[rune::function(instance)]
284+
fn is_success(&self) -> bool {
285+
self.inner.is_success()
286+
}
287+
288+
/// Check if status is within 300-399.
289+
#[inline]
290+
#[rune::function(instance)]
291+
fn is_redirection(&self) -> bool {
292+
self.inner.is_redirection()
293+
}
294+
295+
/// Check if status is within 400-499.
296+
#[inline]
297+
#[rune::function(instance)]
298+
fn is_client_error(&self) -> bool {
299+
self.inner.is_client_error()
300+
}
301+
302+
/// Check if status is within 500-599.
303+
#[inline]
304+
#[rune::function(instance)]
305+
fn is_server_error(&self) -> bool {
306+
self.inner.is_server_error()
307+
}
308+
}
309+
310+
/// Represents a version of the HTTP spec.
311+
#[derive(Debug, Any, PartialEq, Eq, PartialOrd, Ord)]
312+
#[rune(item = ::http)]
313+
pub struct Version {
314+
inner: reqwest::Version,
315+
}
316+
317+
impl Version {
318+
#[rune::function(instance, protocol = STRING_DISPLAY)]
319+
fn string_display(&self, f: &mut Formatter) -> VmResult<()> {
320+
rune::vm_write!(f, "{:?}", self.inner);
321+
VmResult::Ok(())
322+
}
193323
}
194324

195325
/// A builder to construct the properties of a Request.
@@ -239,6 +369,63 @@ impl RequestBuilder {
239369
}
240370
}
241371

372+
/// Enable basic authentication in the request.
373+
///
374+
/// ```rune,no_run
375+
/// let client = http::Client::new();
376+
///
377+
/// let response = client.get("http://example.com")
378+
/// .basic_auth("admin", Some("good password"))
379+
/// .send()
380+
/// .await?;
381+
///
382+
/// let response = response.text().await?;
383+
/// ```
384+
#[rune::function]
385+
fn basic_auth(self, username: &str, password: Option<Ref<str>>) -> Self {
386+
Self {
387+
request: self.request.basic_auth(username, password.as_deref()),
388+
}
389+
}
390+
391+
/// Enable bearer authentication in the request.
392+
///
393+
/// ```rune,no_run
394+
/// let client = http::Client::new();
395+
///
396+
/// let response = client.get("http://example.com")
397+
/// .bearer_auth("A1B2C3D4E5")
398+
/// .send()
399+
/// .await?;
400+
///
401+
/// let response = response.text().await?;
402+
/// ```
403+
#[rune::function]
404+
fn bearer_auth(self, token: &str) -> Self {
405+
Self {
406+
request: self.request.bearer_auth(token),
407+
}
408+
}
409+
410+
/// Set version in the request.
411+
///
412+
/// ```rune,no_run
413+
/// let client = http::Client::new();
414+
///
415+
/// let response = client.get("http://example.com")
416+
/// .version(Version::HTTP_2)
417+
/// .send()
418+
/// .await?;
419+
///
420+
/// let response = response.text().await?;
421+
/// ```
422+
#[rune::function]
423+
fn version(self, version: Version) -> Self {
424+
Self {
425+
request: self.request.version(version.inner),
426+
}
427+
}
428+
242429
/// Disable CORS on fetching the request.
243430
///
244431
/// This option is only effective with WebAssembly target.
@@ -312,7 +499,7 @@ impl Client {
312499
///
313500
/// let response = response.text().await?;
314501
/// ```
315-
#[rune::function]
502+
#[rune::function(instance)]
316503
fn get(&self, url: &str) -> RequestBuilder {
317504
RequestBuilder {
318505
request: self.client.get(url),
@@ -327,17 +514,97 @@ impl Client {
327514
/// let client = http::Client::new();
328515
///
329516
/// let response = client.post("https://postman-echo.com/post")
330-
/// .body_bytes(b"Hello World")
517+
/// .body_bytes(b"My post data...")
331518
/// .send()
332519
/// .await?;
333520
///
334521
/// let response = response.json().await?;
335522
/// ```
336-
#[rune::function]
523+
#[rune::function(instance)]
337524
fn post(&self, url: &str) -> RequestBuilder {
338525
let request = self.client.post(url);
339526
RequestBuilder { request }
340527
}
528+
529+
/// Construct a builder to PUT to the given `url`.
530+
///
531+
/// # Examples
532+
///
533+
/// ```rune,no_run
534+
/// let client = http::Client::new();
535+
///
536+
/// let response = client.put("https://postman-echo.com/put")
537+
/// .body_bytes(b"My put data...")
538+
/// .send()
539+
/// .await?;
540+
///
541+
/// let response = response.json().await?;
542+
/// ```
543+
#[rune::function(instance)]
544+
fn put(&self, url: &str) -> RequestBuilder {
545+
let request = self.client.put(url);
546+
RequestBuilder { request }
547+
}
548+
549+
/// Construct a builder to PATCH to the given `url`.
550+
///
551+
/// # Examples
552+
///
553+
/// ```rune,no_run
554+
/// let client = http::Client::new();
555+
///
556+
/// let response = client.patch("https://postman-echo.com/patch")
557+
/// .body_bytes(b"My patch data...")
558+
/// .send()
559+
/// .await?;
560+
///
561+
/// let response = response.json().await?;
562+
/// ```
563+
#[rune::function(instance)]
564+
fn patch(&self, url: &str) -> RequestBuilder {
565+
let request = self.client.patch(url);
566+
RequestBuilder { request }
567+
}
568+
569+
/// Construct a builder to DELETE to the given `url`.
570+
///
571+
/// # Examples
572+
///
573+
/// ```rune,no_run
574+
/// let client = http::Client::new();
575+
///
576+
/// let response = client.delete("https://postman-echo.com/delete")
577+
/// .body_bytes(b"My delete data...")
578+
/// .send()
579+
/// .await?;
580+
///
581+
/// let response = response.json().await?;
582+
/// ```
583+
#[rune::function(instance)]
584+
fn delete(&self, url: &str) -> RequestBuilder {
585+
let request = self.client.delete(url);
586+
RequestBuilder { request }
587+
}
588+
589+
/// Construct a builder to HEAD to the given `url`.
590+
///
591+
/// # Examples
592+
///
593+
/// ```rune,no_run
594+
/// let client = http::Client::new();
595+
///
596+
/// let response = client.head("https://postman-echo.com/head")
597+
/// .body_bytes(b"My head data...")
598+
/// .send()
599+
/// .await?;
600+
///
601+
/// let response = response.json().await?;
602+
/// ```
603+
#[rune::function(instance)]
604+
fn head(&self, url: &str) -> RequestBuilder {
605+
let request = self.client.head(url);
606+
RequestBuilder { request }
607+
}
341608
}
342609

343610
/// Shorthand for generating a get request.

0 commit comments

Comments
 (0)