Skip to content

Commit

Permalink
Merge pull request #4 from Champii/sql_macro
Browse files Browse the repository at this point in the history
Sql macro
  • Loading branch information
Champii committed Oct 29, 2022
2 parents b2f46b9 + 00559da commit 26bdcf5
Show file tree
Hide file tree
Showing 57 changed files with 7,753 additions and 608 deletions.
113 changes: 92 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Reactive isomorphic rust web framework.

---

## Index

1. [Introduction](#introduction)
Expand All @@ -10,6 +12,8 @@ Reactive isomorphic rust web framework.
4. [Quick tour](#quick-tour)
5. [Todo list](#todo-list)

---

## Introduction

Work in progress, this is still a naive early prototype.
Expand All @@ -34,6 +38,8 @@ to send push notifications over websocket to every client watching for thoses ch

Visit the [examples](https://github.com/Champii/Comet/tree/master/examples) folder.

---

## Features

- Isomorphic client/server
Expand All @@ -44,10 +50,13 @@ Visit the [examples](https://github.com/Champii/Comet/tree/master/examples) fold
- Websocket
- Auto procol generation
- Convenient wrapper binary
- Zero boilerplate
- (Almost) Zero boilerplate
- Clean Codebase (Yeaaah, ok, this one is a lie)
- Fast (Soon™)
- Client cache (Soon™)

---

## Getting started

### Install Comet Binary and dependencies
Expand All @@ -68,10 +77,9 @@ If not found on your system, Comet will install these following crates using `ca
$> comet new my_counter && cd my_counter
```

This newly generated project contains all you need to get started. The only file you have to care about for now is `src/lib.rs`, this is your entry point.
Conveniently, the generated file is already the simpliest incrementing counter you can think of.
This newly generated project contains all you need to get started. Your journey starts with `src/main.rs`.
Conveniently, this generated file is already the simpliest incrementing counter you can think of:

The default generated file `src/lib.rs` :

```rust
// The mandatory imports
Expand Down Expand Up @@ -131,8 +139,18 @@ This will download and install the tools it needs to build and run your crate.

Then go go to [http://localhost:8080](http://localhost:8080)

---

## Quick tour

- [Easy definition of the dom](#easy-definition-of-the-dom)
- [Use conditional rendering and loops](#use-conditional-rendering-and-loops)
- [Bind your variables to `input` fields that react to events](#bind-your-variables-to-input-fields-that-react-to-events)
- [Embed your components between them](#embed-your-components-between-them)
- [Database persistence for free](#database-persistance-for-free)
- [Remote procedure calls](#remote-procedure-calls)
- [Database queries](#database-queries)

### Easy definition of the dom

```rust
Expand Down Expand Up @@ -160,7 +178,7 @@ component! {

```

### Use conditional rendering and loops directly from within the view
### Use conditional rendering and loops

```rust
struct MyComponent {
Expand Down Expand Up @@ -198,7 +216,7 @@ component! {
}
```

### Bind you variables to `input` fields that react to events
### Bind your variables to `input` fields that react to events

This is exclusive to `input` fields for now
The whole component is re-rendered on input's blur event (unfocus).
Expand Down Expand Up @@ -247,9 +265,19 @@ component! {
}
```

### Database persistance for free
### Database persistence for free

All the previous examples until now were client-side only. Its time to introduce some persistance.

Note: This one is still a proof of concept, this needs work.
Deriving with the `#[model]` macro gives you access to many default DB methods implemented for your types:
- async Self::fetch(i32) -> Result<T, String>
- async Self::list() -> Result<Vec<T>, String>;
- async self.save() -> Result<(), String>;
- async Self::delete(i32) -> Result<(), String>;

The `String` error type is meant to change into a real error type soon.

You have a way to add your own database query methods, please read [Database queries](#database-queries) below.

```rust
// You just have to add this little attribute to your type et voila !
Expand All @@ -261,62 +289,105 @@ struct Todo {
}

impl Todo {
fn toggle(&mut self) {
pub async fn toggle(&mut self) {
self.completed = !self.completed;

// This will save the model in the db
self.save();
self.save().await;
}
}

component! {
Todo,
div {
p {
{ self.id }
{ self.title }
{ self.completed }
button @click: { self.toggle() } {
button @click: { self.toggle().await } {
{ "Toggle" }
}
}
}
}

// This will create a new Todo in db every time this program runs
comet!(Todo::create());
comet!(Todo::default().create().await.unwrap());
```

### Remote procedure calls

Note: The structs involved in the `#[rpc]` macro MUST be accessible from the root module (i.e. `src/main.rs`)

```rust
use comet::prelude::*;

// If you have other mods that use `#[rpc]`, you have to import them explicitly in the root (assuming this file is the root)
// This is a limitation that will not last, hopefully
mod other_mod;
use other_mod::OtherComponent;

#[model]
#[derive(Default)]
pub struct Counter {
pub count: i32,
}

// This attribute indicated that all the following methods are to be treated as RPC
// This attribute indicate that all the following methods are to be treated as RPC
// These special methods are only executed server side
#[rpc]
impl Counter {
// The RPC method MUST be async (at least for not)
// They also cannot take a mutable reference on self (yet)
pub async fn remote_increment(&self) -> i32 {
self.count + 1
// The RPC methods MUST be async (at least for now)
pub async fn remote_increment(&mut self) {
self.count += 1;

self.save().await;
}
}

component! {
Counter,
button @click: { self.count = self.remote_increment().await } {
button @click: { self.remote_increment().await } {
{ self.count }
}
}

comet!(Counter::default());
comet!(Counter::default().create().await.unwrap());
```

### Database queries

When dealing with Database queries, it is obvious that they should only be executed server side.
The most simple way to define a new one is with the macro `#[sql]`, that uses `#[rpc]` underneath.

All your models have been augmented with auto-generated diesel bindings, so you can use a familiar syntax.
There will be a way to give raw SQL in the near future.

```rust
#[model]
#[derive(Default, Debug)]
pub struct Todo {
pub title: String,
pub completed: bool,
}

#[sql]
impl Todo {
pub async fn db_get_all(limit: u16) -> Vec<Todo> {
// The diesel schema has been generated for you
use crate::schema::todos;

// You don't have to actually execute the query, all the machinery of creating a db connection
// and feeding it everywhere have been abstracted away so you can concentrate on what matters
todos::table.select(todos::all_columns).limit(limit as i64)
}
}
```

Soon there will also be a `#[watch]` attribute that will trigger the reactive redraw when your model change

---

## Todo List
- Allow for iterators inside html
- Allow to mix attributes, styles and events
Expand All @@ -343,6 +414,6 @@ comet!(Counter::default());
- [ ] The isomorphic db model through websocket
- [ ] The #[model] proc macro that generates basic model queries
- [ ] An abstract ws server/client
- [ ] The auto-proto macro
- [X] The reactive/listening part of the db [reactive-postgres-rs](https://github.com/Champii/reactive-postgres-rs)
- [ ] The auto-proto macro
- [X] The reactive/listening part of the db [reactive-postgres-rs](https://github.com/Champii/reactive-postgres-rs)

1 change: 1 addition & 0 deletions comet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pharos = "0.5"
wasm-bindgen-futures = "0.4.30"
console_error_panic_hook = "0.1.7"
tokio = { version = "1.21.2", features = ["sync"] }
diesel = { version = "2.0.2", default-features = false }


[dev-dependencies]
Expand Down
6 changes: 2 additions & 4 deletions comet/src/core/app.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use std::{cell::RefCell, rc::Rc};

use crate::prelude::*;

pub struct App<Comp, Msg>
where
Comp: Component<Msg>,
Msg: Clone + 'static,
{
pub root: Rc<RefCell<Box<Comp>>>,
pub root: Shared<Comp>,
phantom: std::marker::PhantomData<Msg>,
}

Expand All @@ -16,7 +14,7 @@ where
Comp: Component<Msg>,
Msg: Clone,
{
pub fn new(root: Rc<RefCell<Box<Comp>>>) -> Self {
pub fn new(root: Shared<Comp>) -> Self {
Self {
root,
phantom: std::marker::PhantomData,
Expand Down
7 changes: 4 additions & 3 deletions comet/src/core/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ where

spawn_local(async move {
component2
.borrow_mut()
.write()
.await
.update_bindings(bindings_clone.clone());

if let Some(msg) = msg {
let component3 = component2.clone();
component3.borrow_mut().update(msg).await;
component3.write().await.update(msg).await;
}

let parent3 = parent2.clone();
Expand All @@ -47,7 +48,7 @@ where
})
};

let view = component.borrow().view(cb, bindings.clone());
let view = component.read().await.view(cb, bindings.clone());

parent.set_inner_html("");
parent.append_child(&view).unwrap();
Expand Down
5 changes: 4 additions & 1 deletion comet/src/core/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ pub use derive_more::*;
pub use async_trait::async_trait;
pub use lazy_static::lazy_static;

pub use diesel;
pub use diesel::prelude::*;

pub use paste::paste;
pub use serde;
pub use serde::{Deserialize, Serialize};
Expand All @@ -18,7 +21,7 @@ pub use crate::core::app::*;
pub use crate::core::component::*;
pub use crate::core::shared::*;

pub use crate::core::proto::{Message, Proto};
pub use crate::core::proto::{Message, ProtoTrait};
pub use crate::core::utils::*;
pub use crate::core::*;

Expand Down
4 changes: 2 additions & 2 deletions comet/src/core/proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ impl Message {
}

#[async_trait]
pub trait Proto {
type Response: Proto + Send + Serialize + DeserializeOwned;
pub trait ProtoTrait {
type Response: ProtoTrait + Send + Serialize + DeserializeOwned;

async fn dispatch(self) -> Option<Self::Response>
where
Expand Down
11 changes: 6 additions & 5 deletions comet/src/core/shared.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::ops::Deref;
use std::{cell::RefCell, rc::Rc};
use std::sync::Arc;
use tokio::sync::RwLock;

#[derive(Default)]
pub struct Shared<T>(pub Rc<RefCell<Box<T>>>);
#[derive(Default, Debug)]
pub struct Shared<T>(pub Arc<RwLock<Box<T>>>);

impl<T> Clone for Shared<T> {
fn clone(&self) -> Self {
Expand All @@ -12,12 +13,12 @@ impl<T> Clone for Shared<T> {

impl<T> From<T> for Shared<T> {
fn from(t: T) -> Self {
Self(Rc::new(RefCell::new(Box::new(t))))
Self(Arc::new(RwLock::new(Box::new(t))))
}
}

impl<T> Deref for Shared<T> {
type Target = RefCell<Box<T>>;
type Target = RwLock<Box<T>>;

fn deref(&self) -> &Self::Target {
self.0.as_ref()
Expand Down
4 changes: 3 additions & 1 deletion comet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ pub mod wasm;
#[cfg(not(target_arch = "wasm32"))]
pub mod server;

pub use prelude::comet;

pub async fn run<Comp, Msg>(_root: Comp)
where
Comp: Component<Msg>,
Msg: Clone + 'static,
{
#[cfg(target_arch = "wasm32")]
App::new(Rc::new(RefCell::new(Box::new(_root)))).run().await;
App::new(_root.into()).run().await;
}
4 changes: 2 additions & 2 deletions comet/src/server/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::fmt::Debug;
use futures::stream::SplitSink;
use std::sync::Arc;

use crate::core::prelude::Proto;
use crate::core::prelude::ProtoTrait;

use super::universe::Universe;

Expand Down Expand Up @@ -37,7 +37,7 @@ impl Client {
self.session_id = session_id;
}

pub async fn handle_msg<P: Proto + Send + Serialize + DeserializeOwned + Debug>(&self, msg: Vec<u8>) {
pub async fn handle_msg<P: ProtoTrait + Send + Serialize + DeserializeOwned + Debug>(&self, msg: Vec<u8>) {
let msg = crate::Message::from_bytes(&msg);

let proto = P::from_bytes(&msg.msg);
Expand Down
3 changes: 1 addition & 2 deletions comet/src/server/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
pub use tokio;

pub use diesel;
pub use diesel::prelude::*;
pub use tokio::task::spawn_local;
Loading

0 comments on commit 26bdcf5

Please sign in to comment.