Skip to content

Commit

Permalink
Merge pull request #80 from ngyn-rs/dev
Browse files Browse the repository at this point in the history
chore(release): v0.3.1 - Transducers
  • Loading branch information
elcharitas authored Apr 13, 2024
2 parents b52ed2f + 86cfe23 commit 46aa0ff
Show file tree
Hide file tree
Showing 24 changed files with 348 additions and 92 deletions.
21 changes: 13 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[workspace]
members = [
"crates/*",
"examples/*",
]
members = ["crates/*", "examples/*"]
resolver = "2"
94 changes: 58 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,88 @@
# Ngyn
# ngyn (`en·jn`)

> A progressive Rust Framework for building efficient and modularized backend applications
[![Crates.io](https://img.shields.io/crates/v/ngyn.svg)](https://crates.io/crates/ngyn)
[![Docs.rs](https://docs.rs/ngyn/badge.svg)](https://ngyn.rs)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.md)
![MSRV](https://img.shields.io/badge/MSRV-1.63-blue)

Ngyn is a powerful and progressive Rust framework crafted for the development of efficient and modularized backend applications. With a focus on performance, reliability, and flexibility, Ngyn empowers developers to build robust server-side solutions with ease. Ngyn comes packed with powerful macros, utilities, and features that make it easy to build performant and secure applications.
A progressive framework in [Rust](https://www.rust-lang.org/) for building scalable web applications through an opinionated architecture.

## Features

- **Progressive Enhancement:** Ngyn embraces the philosophy of progressive enhancement, allowing developers to start with a foundational set of features and progressively enhance the application as needed. This ensures a smooth and adaptable development process.

- **Efficiency at Core:** Ngyn is designed to prioritize efficiency in resource utilization, providing a performant environment for backend applications. Whether handling data processing, managing business logic, or interfacing with databases, Ngyn ensures optimal performance.

- **Modular Architecture:** Ngyn encourages a modularized approach to application development. Break down your backend logic into independent and reusable modules, promoting code organization, maintainability, and scalability. Ngyn's modular architecture facilitates collaboration among developers working on distinct components.

- **Optional Async:** Ngyn provides support for async operations out of the box through controllers. However, it's 100% optional.
More information about Ngyn can be found in the [documentation](https://ngyn.rs).

- **Powerful Macros:** Ngyn provides a set of powerful macros that simplify common tasks. From defining routes to creating middleware, Ngyn's macros make it easy to build robust applications.

- **Lightweight:** Ngyn is lightweight and leaves a minimal footprint, making it a great choice for projects of all sizes. Ngyn's lightweight nature ensures that your application is not bogged down by unnecessary bloat, yet still provides the features you need to build a robust backend.
## Features

- **Fully Extensible:** Ngyn allows you to build your own platform engines or make use of any of the built-in `vercel` or `tide` platform engines.
- Macro API for organizing your application into reusable components
- Built-in dependency injection for managing your application's dependencies
- Asynchronous middleware for handling requests and responses
- Asynchronous routing for defining your application's endpoints
- [Platform-agnostic](#platform-agnosticism) for supporting multiple libraries and frameworks

## Getting Started
Please note that Ngyn is still in its early stages of development, and the API is subject to change.

### Installation
## Example

To get started with Ngyn, simply include the framework in your Rust project by adding the following to your `Cargo.toml`:
This example demonstrates how to create a simple web server using Ngyn and [Tide](https://docs.rs/tide). To use Ngyn with Tide, you must enable the `tide` feature in your `Cargo.toml` file.

```toml
[dependencies]
ngyn = "0.3.0"
ngyn = { version = "0.3.0", features = ["tide"] }
nject = "0.3.0"
```

### Example Usage

Here is a simple example of a Ngyn application without any of the more advanced features.

```rust
use ngyn::{module, NgynFactory, NgynRequest, NgynResponse, Result};
And here's the code:

```rust ignore
use ngyn::{
platforms::{NgynApplication, Result},
prelude::*
};

#[controller]
struct MyController;

#[routes]
impl MyController {
#[get("/")]
fn index(&self) {
"Hello World!".to_string()
}

#[get("/hello/:name")]
fn hello(&self) {
let name = request.param("name").unwrap();
format!("Hello, {}!", name)
}
}

#[module]
#[module(controllers = [MyController])]
struct MyAppModule;

#[ngyn::main]
#[main]
async fn main() -> Result<()> {
let app = NgynFactory::create::<MyAppModule>();

app.get("/", |req: &mut NgynRequest, res: &mut NgynResponse| {
res.send("Hello World!");
});
let app = NgynFactory::<NgynApplication>::create::<MyAppModule>();

app.listen("127.0.0.1:8080").await?;

Ok(())
}
```

## Philosophy

### Description

Ngyn proposes an opinionated, modular, and scalable architecture, largely inspired by [NestJS](https://nestjs.com/) and structured around the concept of modules - discrete, reusable components that collectively shape an application. These modules, each addressing specific functionalities, can be nested to form a functional hierarchy. This modular design simplifies application organization and enhances reusability across various projects.

### Platform Agnosticism

A platform (more properly called platform engine) in Ngyn refers to the underlying library or framework that is used to build your application. For example, you could build your application using [Actix](https://actix.rs/) or [Warp](https://docs.rs/warp) or [Tide](https://docs.rs/tide), and each of these platforms would provide a different set of features for building your application.

By default, Ngyn uses [Tide](https://docs.rs/tide) as its underlying platform. However, you're not limited to this and can choose to also create your own platform engines.

## Contribution

Ngyn is an open-source project, and we welcome contributions from the community. Feel free to report issues, suggest enhancements, or submit pull requests to help us improve Ngyn.

## License

Ngyn is licensed under the [MIT License](LICENSE), allowing you to use, modify, and distribute the framework freely.

Start building efficient and modularized backend applications with Ngyn today!
Ngyn is licensed under the [MIT License](LICENSE.md), allowing you to use, modify, and distribute the framework freely.
6 changes: 3 additions & 3 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ngyn"
version = "0.3.0"
version = "0.3.1"
edition = "2021"
description = "Modular backend framework for web applications"
license = "MIT"
Expand All @@ -11,8 +11,8 @@ path = "src/lib.rs"
[dependencies]
async-std = { version = "1.6.0", features = ["attributes"] }
nject = "0.3.0"
ngyn_macros = { version = "0.3.0", path = "../macros" }
ngyn_shared = { version = "0.3.0", path = "../shared" }
ngyn_macros = { version = "0.3.1", path = "../macros" }
ngyn_shared = { version = "0.3.1", path = "../shared" }
tide = { version = "0.16.0", optional = true }
vercel_runtime = { version = "1.1.0", optional = true }

Expand Down
4 changes: 2 additions & 2 deletions crates/core/src/app/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl<Application: NgynEngine> NgynFactory<Application> {
/// ### Example
///
/// ```
/// use ngyn::{module, NgynFactory, platforms::NgynApplication};
/// use ngyn::{platforms::NgynApplication, prelude::*};
///
/// #[module]
/// pub struct YourAppModule;
Expand All @@ -25,7 +25,7 @@ impl<Application: NgynEngine> NgynFactory<Application> {
let mut module = AppModule::new(vec![]);
let mut server = Application::new();
for controller in module.get_controllers() {
for (path, http_method, handler) in controller.get_routes() {
for (path, http_method, handler) in controller.routes() {
let http_method = HttpMethod::from(http_method);
server.route(
path.as_str(),
Expand Down
5 changes: 5 additions & 0 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
#![doc = include_str!("../README.md")]
#[doc(hidden)]
pub mod app;
#[doc(hidden)]
pub mod platforms;

#[doc(hidden)]
pub mod macros {
pub use async_std::main;
pub use ngyn_macros::*;
pub use nject::injectable as dependency;
}

#[doc(hidden)]
pub mod prelude {
pub use crate::app::*;
pub use crate::macros::*;
Expand Down
4 changes: 2 additions & 2 deletions crates/macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ngyn_macros"
version = "0.3.0"
version = "0.3.1"
edition = "2021"
description = "Modular backend framework for web applications"
license = "MIT"
Expand All @@ -9,7 +9,7 @@ license = "MIT"
nject = "0.3.0"
syn = "2.0.29"
quote = "1.0.33"
ngyn_shared = { version = "0.3.0", path = "../shared" }
ngyn_shared = { version = "0.3.1", path = "../shared" }

[lib]
path = "src/lib.rs"
Expand Down
12 changes: 6 additions & 6 deletions crates/macros/src/common/controller_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@ pub fn controller_macro(args: TokenStream, input: TokenStream) -> TokenStream {

async fn __handle_route(
&self,
handler: String,
req: &mut ngyn::prelude::NgynRequest,
res: &mut ngyn::prelude::NgynResponse,
_handler: &str,
_req: &mut ngyn::prelude::NgynRequest,
_res: &mut ngyn::prelude::NgynResponse,
) {
// TODO: Handle routes
// This is a placeholder for the routing logic of the controller.
}
}

Expand All @@ -111,14 +111,14 @@ pub fn controller_macro(args: TokenStream, input: TokenStream) -> TokenStream {
controller
}

fn get_routes(&self) -> Vec<(String, String, String)> {
fn routes(&self) -> Vec<(String, String, String)> {
use ngyn::prelude::NgynControllerRoutePlaceholder;
Self::routes.iter().map(|(path, method, handler)| {
(path.to_string(), method.to_string(), handler.to_string())
}).collect()
}

async fn handle(&self, handler: String, req: &mut ngyn::prelude::NgynRequest, res: &mut ngyn::prelude::NgynResponse) {
async fn handle(&self, handler: &str, req: &mut ngyn::prelude::NgynRequest, res: &mut ngyn::prelude::NgynResponse) {
use ngyn::prelude::NgynControllerRoutePlaceholder;
self.middlewares.iter().for_each(|middleware| {
middleware.handle(req, res);
Expand Down
49 changes: 46 additions & 3 deletions crates/macros/src/common/route_macro.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{ItemFn, Signature};
use syn::{ItemFn, Receiver, Signature};

pub fn route_macro(_args: TokenStream, raw_input: TokenStream) -> TokenStream {
let ItemFn {
Expand All @@ -24,19 +24,62 @@ pub fn route_macro(_args: TokenStream, raw_input: TokenStream) -> TokenStream {
..
} = sig;

let transducers: Vec<_> = inputs
.iter()
.map(|input| {
if let syn::FnArg::Typed(pat) = input {
let ty = &pat.ty;
let pat = &pat.pat;
if let syn::Type::Path(path) = *ty.clone() {
let path = &path.path;
quote! {
let mut #pat: #path = ngyn::prelude::Transducer::reduce::<#path>(request, response);
}
} else {
panic!("Expected a valid struct");
}
} else {
quote! {}
}
})
.collect();

// initial self varn obtained from the first input
let self_var = match inputs.iter().next() {
Some(syn::FnArg::Receiver(receiver)) => {
let Receiver {
reference,
mutability,
self_token,
..
} = receiver;
if reference.is_some() {
if mutability.is_some() {
quote! { &mut #self_token }
} else {
quote! { &#self_token }
}
} else {
quote! { #self_token }
}
}
_ => quote! {},
};

let return_val = match output {
syn::ReturnType::Type(_, _) => quote! {},
_ => quote! { return ngyn::prelude::NgynBody::None; },
};

let output = match output {
syn::ReturnType::Type(_, ty) => quote! { -> #ty }, // TODO: Handle other types aside NgynBody
syn::ReturnType::Type(_, ty) => quote! { -> #ty },
_ => quote! { -> ngyn::prelude::NgynBody },
};

let expanded = quote! {
#(#attrs)*
#vis async #fn_token #ident(#inputs) #output {
#vis async #fn_token #ident(#self_var, request: &mut ngyn::prelude::NgynRequest, response: &mut ngyn::prelude::NgynResponse) #output {
#(#transducers)*
#block
#return_val
}
Expand Down
9 changes: 7 additions & 2 deletions crates/macros/src/common/routes_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,13 @@ pub fn routes_macro(raw_input: TokenStream) -> TokenStream {
];
#(#items)*

async fn __handle_route(&self, handler: String, req: &mut ngyn::prelude::NgynRequest, res: &mut ngyn::prelude::NgynResponse) {
match handler.as_str() {
async fn __handle_route(
&self,
handler: &str,
req: &mut ngyn::prelude::NgynRequest,
res: &mut ngyn::prelude::NgynResponse
) {
match handler {
#(#handle_routes),*
_ => {
res.set_status(404);
Expand Down
Loading

0 comments on commit 46aa0ff

Please sign in to comment.