Skip to content

Commit

Permalink
more rpc-rs stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
RedstoneWizard08 committed Jul 10, 2024
1 parent 0d15141 commit 0f5722e
Show file tree
Hide file tree
Showing 4 changed files with 9 additions and 82 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ webui = { path = "./apps/webui" }
bindings = { path = "./apps/bindings" }
commands = { path = "./crates/commands" }
plugins = { path = "./crates/plugins" }
rpc-rs = { path = "./crates/rpc-rs", features = ["axum", "tauri"] }
rpc-rs = { path = "./crates/rpc-rs", features = ["axum", "tauri", "prisma"] }

[workspace]
resolver = "2"
Expand Down
1 change: 1 addition & 0 deletions crates/rpc-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ authors.workspace = true
default = []
axum = ["dep:axum", "dep:http-body-util"]
tauri = ["dep:tauri"]
prisma = []

[dependencies]
specta.workspace = true
Expand Down
85 changes: 4 additions & 81 deletions crates/rpc-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,88 +10,10 @@ for use with Web technologies, such as JavaScript and TypeScript. It follows the
CRUD (Create, Read, Update, Delete) pattern, and its syntax allows developers to easily
create complex applications with relatively simple code.

## Syntax
## Examples

```rust
//! This code may not be completely accurate! I haven't yet built the library and this
//! is all plans! I also don't have ANY intellisense in a markdown file, so I can't check
//! for dumb syntactical errors!

use std::sync::Arc;
use serde::{Serialize, Deserialize};
use specta::Type;
use database::{prisma::{PrismaClient, game}, Game};
use rpc_rs::{Router, Module, ModuleBuilder, PrismaObject};

// The `PrismaObject` derive macro and trait handle the `db_params` function,
// which transforms this struct into a `Vec<SetParam>`. This struct is also
// read from the request body via Serde, hence the `Deserialize` derive macro
// being used here. The `#[prisma(module = ...)]` helper ensures that the derive
// macro finds the correct place to resolve all of the `SetParam` creation
// functions.
#[derive(Debug, Clone, Serialize, Deserialize, Type, PrismaObject)]
#[prisma(module = "database::prisma::game")]
pub struct GameCreation {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub curseforge: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thunderstore: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub spacedock: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ckan: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modrinth: Option<bool>,
}

pub fn games_module(mut module: ModuleBuilder<State = Arc<PrismaClient>>) -> Module<State = Arc<PrismaClient>> {
module
// This closure is: FnMut(ModuleBuilder::State, GameCreation) -> impl Future<Output = Result<Game, Error>> + Send + Sync + 'static
// The error conversion is handled automatically (via traits)
.create(|db, data: GameCreation| (async move {
// [C]RUD
db.game().create(data.name.clone(), data.db_params()).exec().await
})()) // <- Async closures have to be executed to get the `impl Future<...>`.
// This closure is: FnMut(ModuleBuilder::State, i32) -> impl Future<Output = Result<Game, Error>> + Send + Sync + 'static
.read(|db, id: i32| (async move { // The `id: i32` can be `_: ()` if you don't need any input here.
// C[R]UD
db.game().find_first(vec![game::id::equals(id)]).exec().await
})())
.update(...) // Insert your own functionality here.
.delete(...)
// All the operations get assigned to the `Option<...>` fields in the `ModuleBuilder`
// struct. These will get reassigned to the ones in the `Module` struct and finalized
// when the `build` method is called.
.build()
}

pub fn build_router() -> Router<Arc<PrismaClient>> {
Router::<Arc<PrismaClient>>::new()
.attach(games_module) // Accepts: FnOnce(ModuleBuilder<State = Router::State>) -> Module<State = Router::State>
// This closure is: FnMut(Router::State, ()) -> impl Future<Output = Result<Vec<Game>, Error>> + Send + Sync + 'static
.query("games", |db, _: ()| (async move { // This is useful if you just need a read function.
db.game().find_many(vec![]).exec().await
})())
// The `finish` function finalizes all the state holders and prepares it to be
// mounted with either `axum` or `tauri`.
.finish()
}

#[tokio::main]
pub async fn main() {
let router = build_router();
let db = Arc::new(PrismaClient::_builder().build().await.unwrap());
// Creating the endpoint for axum or the plugin for tauri requires the state object.
let endpoint = router.axum(db);

let router = ...; // Axum stuff.

router.nest("/rpc", endpoint);

// Start your app with axum!
}
```
See [here](https://github.com/RedstoneWizard08/Wormhole/blob/main/crates/commands/src/router.rs)
for a great example of how to use this library.

## Integrations

Expand All @@ -102,3 +24,4 @@ rpc-rs integrates extremely well with the following libraries and tools:
- `specta`
- `serde`
- `serde_json`
- `prisma-client-rust` (Custom build)
3 changes: 3 additions & 0 deletions crates/rpc-rs/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/// - `module` - The path to the module inside the generated `prisma` mod.
/// - `container` - The module's name. If you use `client.xxx.create(...)`, the container would be `xxx`.
/// - `primary_key` - The name of the model's primary key.
#[cfg(feature = "prisma")]
#[macro_export]
macro_rules! prisma_single_module {
{
Expand Down Expand Up @@ -64,6 +65,7 @@ macro_rules! prisma_single_module {
/// - `module` - The path to the module inside the generated `prisma` mod.
/// - `container` - The module's name. If you use `client.xxx.create(...)`, the container would be `xxx`.
/// - `primary_key` - The name of the model's primary key.
#[cfg(feature = "prisma")]
#[macro_export]
macro_rules! prisma_multi_module {
{
Expand Down Expand Up @@ -111,6 +113,7 @@ macro_rules! prisma_multi_module {
/// - `module` - The path to the module inside the generated `prisma` mod.
/// - `container` - The module's name. If you use `client.xxx.create(...)`, the container would be `xxx`.
/// - `primary_key` - The name of the model's primary key.
#[cfg(feature = "prisma")]
#[macro_export]
macro_rules! prisma_module {
($router: ident += [$single: expr, $multi: expr] {
Expand Down

0 comments on commit 0f5722e

Please sign in to comment.