~repos /tide-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
Revert "new stuff"
- examples/basic.rs +2 -19
- impl/src/lib.rs +0 -43
- impl/src/route.rs +0 -38
- impl/src/tags.rs +2 -0
- src/lib.rs +1 -34
- tests/lib.rs +1 -163
- tests/ui/fail/unexpected-attribute.stderr +2 -2
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,
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
-->
|
|
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:
|
|
7
|
+
= note: available fields are: `title`
|