~repos /tide-jsx

#rust#proc-macro#jsx

GIT_CONFIG_PARAMETERS="'http.version=HTTP/1.1'" git clone https://git.pyrossh.dev/tide-jsx/.git tide-jsx
Discussions: https://groups.google.com/g/rust-embed-devs

Tide + JSX


examples/basic.rs CHANGED
@@ -2,14 +2,13 @@ use tide::http::mime;
2
2
  use tide::utils::After;
3
3
  use tide::{log, Request, Response};
4
4
  use tide_jsx::html::HTML5Doctype;
5
- use tide_jsx::{component, get, post, routes, rsx, view};
5
+ use tide_jsx::{component, rsx, view};
6
6
 
7
7
  #[component]
8
8
  fn Heading<'title>(title: &'title str) {
9
9
  rsx! { <h1 class={"title"}>{title}</h1> }
10
10
  }
11
11
 
12
- #[get("/")]
13
12
  async fn index(_req: Request<()>) -> tide::Result {
14
13
  view! {
15
14
  <>
@@ -26,22 +25,6 @@ async fn index(_req: Request<()>) -> tide::Result {
26
25
  }
27
26
  }
28
27
 
29
- #[post("/submit")]
30
- async fn submit(mut req: Request<()>) -> tide::Result {
31
- let body = req.body_string().await?;
32
- view! {
33
- <>
34
- <HTML5Doctype />
35
- <html>
36
- <body>
37
- <h1>{"Submitted"}</h1>
38
- <p>{format!("Received: {}", body)}</p>
39
- </body>
40
- </html>
41
- </>
42
- }
43
- }
44
-
45
28
  #[async_std::main]
46
29
  async fn main() -> tide::Result<()> {
47
30
  log::start();
@@ -56,7 +39,7 @@ async fn main() -> tide::Result<()> {
56
39
  }
57
40
  Ok(res)
58
41
  }));
59
- routes!(app, [index, submit]);
42
+ app.at("/").get(index);
60
43
  app.listen("127.0.0.1:5000").await?;
61
44
  Ok(())
62
45
  }
impl/src/lib.rs CHANGED
@@ -6,7 +6,6 @@ mod element;
6
6
  mod element_attribute;
7
7
  mod element_attributes;
8
8
  mod function_component;
9
- mod route;
10
9
  mod tags;
11
10
 
12
11
  use element::Element;
@@ -51,45 +50,3 @@ pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream {
51
50
  let f = parse_macro_input!(item as syn::ItemFn);
52
51
  function_component::create_function_component(f)
53
52
  }
54
-
55
- #[proc_macro_attribute]
56
- #[proc_macro_error]
57
- pub fn get(attr: TokenStream, item: TokenStream) -> TokenStream {
58
- route::create_route("get", attr, item)
59
- }
60
-
61
- #[proc_macro_attribute]
62
- #[proc_macro_error]
63
- pub fn post(attr: TokenStream, item: TokenStream) -> TokenStream {
64
- route::create_route("post", attr, item)
65
- }
66
-
67
- #[proc_macro_attribute]
68
- #[proc_macro_error]
69
- pub fn put(attr: TokenStream, item: TokenStream) -> TokenStream {
70
- route::create_route("put", attr, item)
71
- }
72
-
73
- #[proc_macro_attribute]
74
- #[proc_macro_error]
75
- pub fn delete(attr: TokenStream, item: TokenStream) -> TokenStream {
76
- route::create_route("delete", attr, item)
77
- }
78
-
79
- #[proc_macro_attribute]
80
- #[proc_macro_error]
81
- pub fn patch(attr: TokenStream, item: TokenStream) -> TokenStream {
82
- route::create_route("patch", attr, item)
83
- }
84
-
85
- #[proc_macro_attribute]
86
- #[proc_macro_error]
87
- pub fn head(attr: TokenStream, item: TokenStream) -> TokenStream {
88
- route::create_route("head", attr, item)
89
- }
90
-
91
- #[proc_macro_attribute]
92
- #[proc_macro_error]
93
- pub fn options(attr: TokenStream, item: TokenStream) -> TokenStream {
94
- route::create_route("options", attr, item)
95
- }
impl/src/route.rs DELETED
@@ -1,38 +0,0 @@
1
- use proc_macro::TokenStream;
2
- use quote::quote;
3
- use syn::LitStr;
4
-
5
- pub fn create_route(method: &str, attr: TokenStream, item: TokenStream) -> TokenStream {
6
- let path = match syn::parse::<LitStr>(attr) {
7
- Ok(lit) => lit,
8
- Err(err) => return TokenStream::from(err.to_compile_error()),
9
- };
10
- let f = match syn::parse::<syn::ItemFn>(item) {
11
- Ok(f) => f,
12
- Err(err) => return TokenStream::from(err.to_compile_error()),
13
- };
14
-
15
- let fn_name = &f.sig.ident;
16
- let vis = &f.vis;
17
- let block = &f.block;
18
- let inputs = &f.sig.inputs;
19
- let output = &f.sig.output;
20
- let attrs = &f.attrs;
21
- let asyncness = &f.sig.asyncness;
22
-
23
- let method_str = method;
24
-
25
- TokenStream::from(quote! {
26
- #[allow(non_camel_case_types)]
27
- #vis struct #fn_name;
28
-
29
- impl #fn_name {
30
- pub const PATH: &'static str = #path;
31
- pub const METHOD: &'static str = #method_str;
32
-
33
- #(#attrs)*
34
- #vis #asyncness fn handler(#inputs) #output
35
- #block
36
- }
37
- })
38
- }
impl/src/tags.rs CHANGED
@@ -8,6 +8,7 @@ pub struct OpenTag {
8
8
  pub name: syn::Path,
9
9
  pub attributes: ElementAttributes,
10
10
  pub self_closing: bool,
11
+ pub is_custom_element: bool,
11
12
  }
12
13
 
13
14
  fn name_or_fragment(maybe_name: Result<syn::Path>) -> syn::Path {
@@ -39,6 +40,7 @@ impl Parse for OpenTag {
39
40
  name,
40
41
  attributes,
41
42
  self_closing,
43
+ is_custom_element,
42
44
  })
43
45
  }
44
46
  }
src/lib.rs CHANGED
@@ -10,37 +10,4 @@ pub use self::render::Render;
10
10
  pub use fragment::Fragment;
11
11
  pub use simple_element::SimpleElement;
12
12
  pub use text_element::Raw;
13
- pub use tide_jsx_impl::{component, delete, get, head, html, options, patch, post, put, rsx, view};
13
+ pub use tide_jsx_impl::{component, html, rsx, view};
14
-
15
- #[macro_export]
16
- macro_rules! routes {
17
- ($app:expr, [$($route:ident),* $(,)?]) => {{
18
- $(
19
- $crate::__register_route!($app, $route);
20
- )*
21
- }};
22
- }
23
-
24
- #[macro_export]
25
- #[doc(hidden)]
26
- macro_rules! __register_route {
27
- ($app:expr, $route:ident) => {
28
- if $route::METHOD == "get" {
29
- $app.at($route::PATH).get($route::handler);
30
- } else if $route::METHOD == "post" {
31
- $app.at($route::PATH).post($route::handler);
32
- } else if $route::METHOD == "put" {
33
- $app.at($route::PATH).put($route::handler);
34
- } else if $route::METHOD == "delete" {
35
- $app.at($route::PATH).delete($route::handler);
36
- } else if $route::METHOD == "patch" {
37
- $app.at($route::PATH).patch($route::handler);
38
- } else if $route::METHOD == "head" {
39
- $app.at($route::PATH).head($route::handler);
40
- } else if $route::METHOD == "options" {
41
- $app.at($route::PATH).options($route::handler);
42
- } else {
43
- panic!("Unsupported HTTP method: {}", $route::METHOD);
44
- }
45
- };
46
- }
tests/lib.rs CHANGED
@@ -253,9 +253,7 @@ mod other {
253
253
  }
254
254
 
255
255
  mod integration {
256
- use crate::{component, rsx, view};
257
- use tide::Request;
256
+ use crate::view;
258
- use tide_jsx::{delete, get, patch, post, put, routes};
259
257
  use tide_testing::TideTestingExt;
260
258
 
261
259
  #[async_std::test]
@@ -279,164 +277,4 @@ mod integration {
279
277
  );
280
278
  Ok(())
281
279
  }
282
-
283
- #[get("/")]
284
- async fn index(_req: Request<()>) -> tide::Result {
285
- view! { <h1>{"Home"}</h1> }
286
- }
287
-
288
- #[get("/about")]
289
- async fn about(_req: Request<()>) -> tide::Result {
290
- view! { <h1>{"About"}</h1> }
291
- }
292
-
293
- #[post("/submit")]
294
- async fn submit(mut req: Request<()>) -> tide::Result {
295
- let body = req.body_string().await?;
296
- view! { <p>{format!("Received: {}", body)}</p> }
297
- }
298
-
299
- #[put("/update")]
300
- async fn update(_req: Request<()>) -> tide::Result {
301
- view! { <p>{"Updated"}</p> }
302
- }
303
-
304
- #[patch("/patch")]
305
- async fn patch_it(_req: Request<()>) -> tide::Result {
306
- view! { <p>{"Patched"}</p> }
307
- }
308
-
309
- #[delete("/remove")]
310
- async fn remove(_req: Request<()>) -> tide::Result {
311
- view! { <p>{"Deleted"}</p> }
312
- }
313
-
314
- #[async_std::test]
315
- async fn test_get_route_macro() -> tide::Result<()> {
316
- let mut app = tide::new();
317
- routes!(app, [index]);
318
-
319
- let res = app.get("/").recv_string().await?;
320
- assert_eq!(res, "<h1>Home</h1>");
321
- Ok(())
322
- }
323
-
324
- #[async_std::test]
325
- async fn test_multiple_get_routes() -> tide::Result<()> {
326
- let mut app = tide::new();
327
- routes!(app, [index, about]);
328
-
329
- assert_eq!(app.get("/").recv_string().await?, "<h1>Home</h1>");
330
- assert_eq!(app.get("/about").recv_string().await?, "<h1>About</h1>");
331
- Ok(())
332
- }
333
-
334
- #[async_std::test]
335
- async fn test_post_route_macro() -> tide::Result<()> {
336
- let mut app = tide::new();
337
- routes!(app, [submit]);
338
-
339
- let res = app.post("/submit").body_string("hello".into()).recv_string().await?;
340
- assert_eq!(res, "<p>Received: hello</p>");
341
- Ok(())
342
- }
343
-
344
- #[async_std::test]
345
- async fn test_put_route_macro() -> tide::Result<()> {
346
- let mut app = tide::new();
347
- routes!(app, [update]);
348
-
349
- let mut res = app.put("/update").await?;
350
- assert_eq!(res.body_string().await?, "<p>Updated</p>");
351
- Ok(())
352
- }
353
-
354
- #[async_std::test]
355
- async fn test_patch_route_macro() -> tide::Result<()> {
356
- let mut app = tide::new();
357
- routes!(app, [patch_it]);
358
-
359
- let mut res = app.patch("/patch").await?;
360
- assert_eq!(res.body_string().await?, "<p>Patched</p>");
361
- Ok(())
362
- }
363
-
364
- #[async_std::test]
365
- async fn test_delete_route_macro() -> tide::Result<()> {
366
- let mut app = tide::new();
367
- routes!(app, [remove]);
368
-
369
- let mut res = app.delete("/remove").await?;
370
- assert_eq!(res.body_string().await?, "<p>Deleted</p>");
371
- Ok(())
372
- }
373
-
374
- #[async_std::test]
375
- async fn test_all_methods_together() -> tide::Result<()> {
376
- let mut app = tide::new();
377
- routes!(app, [index, about, submit, update, patch_it, remove]);
378
-
379
- assert_eq!(app.get("/").recv_string().await?, "<h1>Home</h1>");
380
- assert_eq!(app.get("/about").recv_string().await?, "<h1>About</h1>");
381
- assert_eq!(
382
- app.post("/submit").body_string("data".into()).recv_string().await?,
383
- "<p>Received: data</p>"
384
- );
385
-
386
- let mut put_res = app.put("/update").await?;
387
- assert_eq!(put_res.body_string().await?, "<p>Updated</p>");
388
-
389
- let mut patch_res = app.patch("/patch").await?;
390
- assert_eq!(patch_res.body_string().await?, "<p>Patched</p>");
391
-
392
- let mut del_res = app.delete("/remove").await?;
393
- assert_eq!(del_res.body_string().await?, "<p>Deleted</p>");
394
-
395
- Ok(())
396
- }
397
-
398
- #[component]
399
- fn Greeting<'name>(name: &'name str) {
400
- rsx! { <h1>{format!("Hello, {}!", name)}</h1> }
401
- }
402
-
403
- #[get("/greet")]
404
- async fn greet(_req: Request<()>) -> tide::Result {
405
- view! { <Greeting name={"World"} /> }
406
- }
407
-
408
- #[async_std::test]
409
- async fn test_route_with_component() -> tide::Result<()> {
410
- let mut app = tide::new();
411
- routes!(app, [greet]);
412
-
413
- assert_eq!(
414
- app.get("/greet").recv_string().await?,
415
- "<h1>Hello, World!</h1>"
416
- );
417
- Ok(())
418
- }
419
-
420
- #[async_std::test]
421
- async fn test_route_returns_html_content_type() -> tide::Result<()> {
422
- let mut app = tide::new();
423
- routes!(app, [index]);
424
-
425
- let res = app.get("/").await?;
426
- assert_eq!(
427
- res.header("content-type").unwrap().as_str(),
428
- tide::http::mime::HTML.to_string()
429
- );
430
- Ok(())
431
- }
432
-
433
- #[async_std::test]
434
- async fn test_wrong_method_returns_not_found() -> tide::Result<()> {
435
- let mut app = tide::new();
436
- routes!(app, [index]);
437
-
438
- let res = app.post("/").await?;
439
- assert_eq!(res.status(), tide::http::StatusCode::MethodNotAllowed);
440
- Ok(())
441
- }
442
280
  }
tests/ui/fail/unexpected-attribute.stderr CHANGED
@@ -1,7 +1,7 @@
1
1
  error[E0560]: struct `Heading<'_>` has no field named `t`
2
- --> tests/ui/fail/unexpected-attribute.rs:9:22
2
+ --> $DIR/unexpected-attribute.rs:9:22
3
3
  |
4
4
  9 | html! { <Heading t={"Hello world!"} /> };
5
5
  | ^ `Heading<'_>` does not have this field
6
6
  |
7
- = note: all struct fields are already assigned
7
+ = note: available fields are: `title`