├── examples ├── README.md ├── weather_app │ ├── .gitignore │ ├── .env.sample │ ├── src │ │ ├── middlewares │ │ │ ├── mod.rs │ │ │ ├── test_middleware.rs │ │ │ └── notfound_middleware.rs │ │ ├── main.rs │ │ └── weather.rs │ ├── README.md │ └── Cargo.toml ├── vercel_app │ ├── .gitignore │ ├── vercel.json │ ├── Cargo.toml │ ├── api │ │ └── [[...all]].rs │ └── public │ │ └── index.html ├── sqlx_app │ ├── .env │ ├── Cargo.toml │ ├── .sqlx │ │ └── query-843923b9a0257cf80f1dff554e7dc8fdfc05f489328e8376513124dfb42996e3.json │ └── src │ │ └── main.rs ├── basic_app │ ├── static │ │ └── logo.png │ ├── docker-compose.yml │ ├── Cargo.toml │ ├── Dockerfile │ └── src │ │ └── main.rs ├── websocket │ ├── Cargo.toml │ └── src │ │ └── main.rs └── graphql_app │ ├── Cargo.toml │ └── src │ └── main.rs ├── sites ├── .gitignore └── docs │ ├── static │ ├── .nojekyll │ └── img │ │ ├── favicon.ico │ │ ├── docusaurus.png │ │ └── docusaurus-social-card.jpg │ ├── versioned_docs │ ├── version-0.4 │ │ ├── advanced │ │ │ ├── routing.md │ │ │ ├── bytes-parsing.md │ │ │ ├── path-prefix.md │ │ │ ├── validation.md │ │ │ ├── _category_.json │ │ │ └── dtos.md │ │ ├── platforms │ │ │ ├── hyper.md │ │ │ ├── vercel.md │ │ │ ├── index.md │ │ │ └── _category_.json │ │ ├── providers │ │ │ ├── context.md │ │ │ ├── state.md │ │ │ ├── transformers.md │ │ │ └── _category_.json │ │ ├── foundations │ │ │ ├── modules.md │ │ │ ├── gates.md │ │ │ ├── index.md │ │ │ ├── middlewares.md │ │ │ └── injectables.md │ │ └── index.md │ └── version-0.5.x │ │ ├── gates.md │ │ ├── getting-started.md │ │ ├── index.md │ │ └── websockets.md │ ├── versions.json │ ├── babel.config.js │ ├── docs │ └── intro.md │ ├── src │ ├── mdx │ │ ├── recma.mjs │ │ ├── remark.mjs │ │ ├── rehype.mjs │ │ └── search.mjs │ ├── lib │ │ └── remToPx.ts │ ├── theme │ │ ├── Layout │ │ │ ├── styles.module.css │ │ │ ├── Provider │ │ │ │ └── index.tsx │ │ │ └── index.tsx │ │ ├── MDXComponents │ │ │ └── Heading.tsx │ │ └── CodeBlock │ │ │ └── Container │ │ │ └── index.tsx │ ├── components │ │ ├── Prose.tsx │ │ ├── GridPattern.tsx │ │ ├── Tag.tsx │ │ ├── Guides.tsx │ │ ├── CodeWrap.tsx │ │ ├── Button.tsx │ │ ├── Feedback.tsx │ │ ├── Heading.tsx │ │ ├── SectionProvider.tsx │ │ └── Footer.tsx │ ├── css │ │ └── custom.css │ └── pages │ │ └── index.tsx │ ├── .prettierrc │ ├── sidebars.ts │ ├── types.d.ts │ ├── tsconfig.json │ ├── .gitignore │ ├── versioned_sidebars │ ├── version-0.4-sidebars.json │ └── version-0.5.x-sidebars.json │ ├── README.md │ ├── eslint.config.mjs │ ├── tailwind.config.ts │ ├── package.json │ └── docusaurus.config.ts ├── rustfmt.toml ├── crates ├── core │ ├── README.md │ ├── src │ │ └── lib.rs │ ├── Cargo.toml │ └── CHANGELOG.md ├── macros │ ├── README.md │ ├── tests │ │ ├── tests.rs │ │ ├── common │ │ │ ├── state.rs │ │ │ └── state.expanded.rs │ │ └── core │ │ │ ├── dto.rs │ │ │ ├── param.rs │ │ │ ├── param.expanded.rs │ │ │ └── dto.expanded.rs │ ├── Cargo.toml │ ├── src │ │ ├── core │ │ │ ├── dto.rs │ │ │ ├── param.rs │ │ │ └── query.rs │ │ ├── common │ │ │ ├── service.rs │ │ │ ├── state.rs │ │ │ └── handler.rs │ │ └── lib.rs │ └── CHANGELOG.md ├── shared │ ├── README.md │ ├── src │ │ ├── core │ │ │ ├── mod.rs │ │ │ ├── statics.rs │ │ │ └── handler.rs │ │ ├── server │ │ │ └── mod.rs │ │ └── lib.rs │ └── Cargo.toml ├── cli │ ├── src │ │ ├── templates │ │ │ ├── mod.hbs │ │ │ ├── module.hbs │ │ │ ├── service.hbs │ │ │ ├── route.hbs │ │ │ ├── gate.hbs │ │ │ ├── controller.hbs │ │ │ └── middleware.hbs │ │ ├── data.rs │ │ └── bin │ │ │ ├── main.rs │ │ │ └── cmd │ │ │ ├── default.rs │ │ │ ├── mod.rs │ │ │ └── new.rs │ ├── CHANGELOG.md │ └── Cargo.toml ├── README.md ├── Cargo.toml ├── ws │ ├── CHANGELOG.md │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── shuttle │ ├── Cargo.toml │ ├── CHANGELOG.md │ └── src │ │ └── lib.rs ├── hyper │ ├── Cargo.toml │ └── CHANGELOG.md ├── vercel │ ├── Cargo.toml │ ├── CHANGELOG.md │ └── src │ │ └── lib.rs ├── swagger │ ├── Cargo.toml │ └── src │ │ └── templates │ │ └── swagger.html └── swagger-macros │ └── Cargo.toml ├── .cargo └── config.toml ├── starter-templates ├── with-basic │ ├── src │ │ ├── routes │ │ │ ├── mod.rs │ │ │ └── echo_route.rs │ │ └── main.rs │ └── Cargo.toml ├── with-vercel │ ├── src │ │ └── main.rs │ ├── api │ │ └── [[...all]].rs │ ├── Cargo.toml │ └── public │ │ └── index.html └── with-ramhorns │ ├── src │ └── main.rs │ └── Cargo.toml ├── Cargo.toml ├── apps └── chip_search │ ├── Cargo.toml │ └── src │ └── main.rs ├── cog.toml ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── release.yml │ ├── version-bump.yml │ ├── pr.yml │ └── publish.yml ├── .gitignore ├── rustwrap.yaml ├── LICENSE.md ├── release.sh ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md /examples/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sites/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sites/docs/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 100 2 | -------------------------------------------------------------------------------- /crates/core/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /crates/macros/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /crates/shared/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /examples/weather_app/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/vercel_app/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "./build" -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/advanced/routing.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/platforms/hyper.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/platforms/vercel.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/providers/context.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/providers/state.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sites/docs/versions.json: -------------------------------------------------------------------------------- 1 | [ 2 | "0.5.x", 3 | "0.4" 4 | ] -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/advanced/bytes-parsing.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/advanced/path-prefix.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/advanced/validation.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/providers/transformers.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/weather_app/.env.sample: -------------------------------------------------------------------------------- 1 | WEATHER_API_KEY=your_api_key 2 | -------------------------------------------------------------------------------- /crates/shared/src/core/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod engine; 2 | pub mod handler; 3 | -------------------------------------------------------------------------------- /starter-templates/with-basic/src/routes/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod echo_route; 2 | -------------------------------------------------------------------------------- /crates/cli/src/templates/mod.hbs: -------------------------------------------------------------------------------- 1 | {{#mods}} 2 | pub mod {{name}}; 3 | {{/mods}}{{initial}} -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/platforms/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- -------------------------------------------------------------------------------- /starter-templates/with-vercel/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /starter-templates/with-ramhorns/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /examples/weather_app/src/middlewares/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod notfound_middleware; 2 | pub mod test_middleware; 3 | -------------------------------------------------------------------------------- /examples/sqlx_app/.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL=postgres://postgres:password@localhost:5432/postgres 2 | SQLX_OFFLINE=true -------------------------------------------------------------------------------- /examples/weather_app/README.md: -------------------------------------------------------------------------------- 1 | # Weather Api 2 | 3 | A sample server showing Ngyn's power in real life apps. 4 | -------------------------------------------------------------------------------- /examples/basic_app/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngyn-rs/ngyn/HEAD/examples/basic_app/static/logo.png -------------------------------------------------------------------------------- /sites/docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngyn-rs/ngyn/HEAD/sites/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /sites/docs/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngyn-rs/ngyn/HEAD/sites/docs/static/img/docusaurus.png -------------------------------------------------------------------------------- /sites/docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /sites/docs/docs/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Ngyn 6 | 7 | > This is WIP for future releases of Ngyn. 8 | -------------------------------------------------------------------------------- /sites/docs/src/mdx/recma.mjs: -------------------------------------------------------------------------------- 1 | import { mdxAnnotations } from 'mdx-annotations' 2 | 3 | export const recmaPlugins = [mdxAnnotations.recma] 4 | -------------------------------------------------------------------------------- /sites/docs/static/img/docusaurus-social-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngyn-rs/ngyn/HEAD/sites/docs/static/img/docusaurus-social-card.jpg -------------------------------------------------------------------------------- /examples/basic_app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | app: 5 | build: 6 | context: . 7 | ports: 8 | - 8080:8080 9 | -------------------------------------------------------------------------------- /crates/macros/tests/tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | pub fn pass_expect_expanded() { 3 | macrotest::expand("tests/core/*.rs"); 4 | macrotest::expand("tests/common/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/advanced/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Advanced", 3 | "position": 3, 4 | "link": { 5 | "type": "generated-index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /sites/docs/src/mdx/remark.mjs: -------------------------------------------------------------------------------- 1 | import { mdxAnnotations } from 'mdx-annotations' 2 | import remarkGfm from 'remark-gfm' 3 | 4 | export const remarkPlugins = [mdxAnnotations.remark, remarkGfm] 5 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/platforms/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Platforms", 3 | "position": 5, 4 | "link": { 5 | "type": "generated-index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /crates/README.md: -------------------------------------------------------------------------------- 1 | # Ngyn Crates 2 | 3 | This directory contains the crates that are used in Ngyn. 4 | 5 | It also contains a specialized `Cargo.toml` file that is used to build releases of Ngyn. 6 | -------------------------------------------------------------------------------- /sites/docs/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": true, 4 | "singleQuote": false, 5 | "tabWidth": 4, 6 | "trailingComma": "es5", 7 | "printWidth": 80 8 | } 9 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/providers/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Data Providers", 3 | "position": 4, 4 | "link": { 5 | "type": "generated-index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /crates/macros/tests/common/state.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate ngyn_macros; 3 | 4 | use ngyn_macros::AppState; 5 | 6 | #[derive(AppState)] 7 | struct TestState { 8 | name: String, 9 | } 10 | -------------------------------------------------------------------------------- /crates/macros/tests/core/dto.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate ngyn_macros; 3 | 4 | use ngyn_macros::Dto; 5 | 6 | #[derive(Dto)] 7 | struct User { 8 | id: i32, 9 | name: String, 10 | } 11 | -------------------------------------------------------------------------------- /crates/macros/tests/core/param.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate ngyn_macros; 3 | 4 | use ngyn_macros::Param; 5 | 6 | #[derive(Param)] 7 | struct UserParam { 8 | id: i32, 9 | name: String, 10 | } 11 | -------------------------------------------------------------------------------- /crates/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["./*"] 3 | resolver = "2" 4 | 5 | [workspace.dependencies] 6 | bytes = "1.9" 7 | http = "1.2" 8 | http-body-util = "0.1" 9 | serde = { version = "1.0", features = ["derive"] } 10 | serde_json = "1.0" 11 | -------------------------------------------------------------------------------- /crates/cli/src/templates/module.hbs: -------------------------------------------------------------------------------- 1 | use ngyn::prelude::*; 2 | 3 | {{#mods}} 4 | use super::{{name}}::{{suffix}}; 5 | {{/mods}} 6 | 7 | #[module(controllers = [{{#mods}}{{suffix}},{{/mods}}])] 8 | /// Module `{{name}}` 9 | pub struct {{name}} {} 10 | -------------------------------------------------------------------------------- /starter-templates/with-basic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "with-basic" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | ngyn = { git = "https://github.com/ngyn-rs/ngyn", branch = "dev" } 8 | tokio = { version = "1", features = ["full"] } 9 | -------------------------------------------------------------------------------- /examples/vercel_app/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": { 3 | "api/**/*.rs": { 4 | "runtime": "vercel-rust@4.0.7" 5 | } 6 | }, 7 | "rewrites": [ 8 | { 9 | "source": "/(.*)", 10 | "destination": "/api/$1" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /crates/cli/src/templates/service.hbs: -------------------------------------------------------------------------------- 1 | use ngyn::prelude::*; 2 | 3 | {{#services}} 4 | use super::{{name}}::{{suffix}}; 5 | {{/services}} 6 | 7 | #[injectable] 8 | pub struct {{name}} { 9 | {{#services}} 10 | pub {{name}}: {{suffix}}, 11 | {{/services}} 12 | } 13 | -------------------------------------------------------------------------------- /examples/weather_app/src/middlewares/test_middleware.rs: -------------------------------------------------------------------------------- 1 | use ngyn::prelude::*; 2 | 3 | pub struct TestMiddleware; 4 | 5 | impl NgynMiddleware for TestMiddleware { 6 | async fn handle(_cx: &mut NgynContext<'_>) { 7 | println!("middleware works"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sites/docs/src/lib/remToPx.ts: -------------------------------------------------------------------------------- 1 | export function remToPx(remValue: number) { 2 | const rootFontSize = 3 | typeof window === 'undefined' 4 | ? 16 5 | : parseFloat(window.getComputedStyle(document.documentElement).fontSize) 6 | 7 | return remValue * rootFontSize 8 | } 9 | -------------------------------------------------------------------------------- /examples/basic_app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_app" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "basic_app" 8 | path = "src/main.rs" 9 | 10 | [dependencies] 11 | ngyn = { version = "0" } 12 | tokio = { version = "1", features = ["full"] } 13 | -------------------------------------------------------------------------------- /examples/basic_app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.81 AS builder 2 | 3 | WORKDIR /app 4 | COPY . . 5 | RUN cargo build --release 6 | 7 | FROM ubuntu:20.04 8 | WORKDIR /app 9 | COPY --from=builder /app/target/release/basic_app . 10 | COPY --from=builder /app/static . 11 | EXPOSE 8080 12 | CMD ["./basic_app"] 13 | -------------------------------------------------------------------------------- /examples/websocket/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "websocket" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | ngyn = { version = "0", path = "../../crates/core" } 8 | ngyn-websocket = { version = "0", path = "../../crates/ws" } 9 | tokio = { version = "1", features = ["full"] } 10 | -------------------------------------------------------------------------------- /starter-templates/with-ramhorns/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "with-ramhorns" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | ngyn = { git = "https://github.com/ngyn-rs/ngyn", branch = "dev" } 8 | ramhorns = { version = "1" } 9 | tokio = { version = "1", features = ["full"] } 10 | -------------------------------------------------------------------------------- /sites/docs/sidebars.ts: -------------------------------------------------------------------------------- 1 | import type { SidebarsConfig } from "@docusaurus/plugin-content-docs"; 2 | 3 | const sidebars: SidebarsConfig = { 4 | // By default, Docusaurus generates a sidebar from the docs folder structure 5 | tutorialSidebar: [{ type: "autogenerated", dirName: "." }], 6 | }; 7 | 8 | export default sidebars; 9 | -------------------------------------------------------------------------------- /sites/docs/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { SearchOptions } from "flexsearch"; 2 | 3 | declare module "@site/src/mdx/search.mjs" { 4 | export type Result = { 5 | url: string; 6 | title: string; 7 | pageTitle?: string; 8 | }; 9 | 10 | export function search(query: string, options?: SearchOptions): Array; 11 | } 12 | -------------------------------------------------------------------------------- /sites/docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@docusaurus/tsconfig", 4 | "compilerOptions": { 5 | "baseUrl": ".", 6 | "jsx": "react-jsx", 7 | "paths": { 8 | "@site/*": [ 9 | "./*" 10 | ] 11 | } 12 | }, 13 | } -------------------------------------------------------------------------------- /crates/cli/src/templates/route.hbs: -------------------------------------------------------------------------------- 1 | use ngyn::prelude::*; 2 | 3 | /// `{{name}}` route 4 | /// 5 | /// # Description 6 | /// This is the `{{name}}` route 7 | /// 8 | /// # Arguments 9 | /// * `_cx` - The context of the current request 10 | pub fn {{name}}<'a>(_cx: &'a mut NgynContext) -> String { 11 | "Process {{name}}".to_string() 12 | } 13 | -------------------------------------------------------------------------------- /sites/docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["apps/chip_search", "crates/*", "examples/*", "starter-templates/*"] 3 | resolver = "2" 4 | 5 | [workspace.dependencies] 6 | bytes = "1.9" 7 | http = "1.2" 8 | http-body-util = "0.1" 9 | serde = { version = "1.0", features = ["derive"] } 10 | serde_json = "1.0" 11 | 12 | [profile.release] 13 | debug = true 14 | -------------------------------------------------------------------------------- /starter-templates/with-basic/src/routes/echo_route.rs: -------------------------------------------------------------------------------- 1 | use ngyn::prelude::*; 2 | 3 | /// `echo_route` route 4 | /// 5 | /// # Description 6 | /// This is the `echo_route` route 7 | /// 8 | /// # Arguments 9 | /// * `_cx` - The context of the current request 10 | pub fn echo_route(_cx: &mut NgynContext) -> String { 11 | "Process echo_route".to_string() 12 | } 13 | -------------------------------------------------------------------------------- /apps/chip_search/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chip_search" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | dotenv = "0.15" 8 | ngyn = { version = "0.5" } 9 | reqwest = { version = "0.12.9", features = ["json"] } 10 | serde = { version = "1.0", features = ["derive"] } 11 | serde_json = "1.0" 12 | tokio = { version = "1", features = ["full"] } 13 | -------------------------------------------------------------------------------- /examples/websocket/src/main.rs: -------------------------------------------------------------------------------- 1 | use ngyn::prelude::*; 2 | use ngyn_websocket::WebsocketApplication; 3 | 4 | #[tokio::main] 5 | async fn main() { 6 | let mut app = WebsocketApplication::default(); 7 | 8 | app.any("/", handler(|_| "Hello")); 9 | 10 | println!("Starting server at ws://127.0.0.1:8080"); 11 | 12 | let _ = app.listen("0.0.0.0:8080"); 13 | } 14 | -------------------------------------------------------------------------------- /starter-templates/with-basic/src/main.rs: -------------------------------------------------------------------------------- 1 | mod routes; 2 | 3 | use ngyn::prelude::*; 4 | use routes::echo_route::echo_route; 5 | 6 | #[tokio::main] 7 | async fn main() { 8 | let mut app = HyperApplication::default(); 9 | 10 | app.get("/", handler(echo_route)); 11 | 12 | println!("Starting server at http://127.0.0.1:8080"); 13 | let _ = app.listen("0.0.0.0:8080").await; 14 | } 15 | -------------------------------------------------------------------------------- /crates/cli/src/templates/gate.hbs: -------------------------------------------------------------------------------- 1 | use ngyn::prelude::*; 2 | 3 | {{#services}} 4 | use super::{{name}}::{{suffix}}; 5 | {{/services}} 6 | 7 | #[injectable] 8 | pub struct {{name}} { 9 | {{#services}} 10 | pub {{name}}: {{suffix}}, 11 | {{/services}} 12 | } 13 | 14 | impl NgynGate for {{name}} { 15 | async fn can_activate(&self, cx: &mut NgynContext) -> bool { 16 | true 17 | } 18 | } -------------------------------------------------------------------------------- /examples/sqlx_app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sqlx_app" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "basic_app" 8 | path = "src/main.rs" 9 | 10 | [dependencies] 11 | dotenv = "0.15.0" 12 | ngyn = { version = "0.5.2", path = "../../crates/core" } 13 | sqlx = { version = "0.7.0", features = [ "runtime-tokio", "postgres", "time"] } 14 | tokio = { version = "1", features = ["full"] } 15 | -------------------------------------------------------------------------------- /crates/cli/src/templates/controller.hbs: -------------------------------------------------------------------------------- 1 | use ngyn::prelude::*; 2 | 3 | {{#services}} 4 | use super::{{name}}::{{suffix}}; 5 | {{/services}} 6 | 7 | #[controller] 8 | pub struct {{name}} { 9 | {{#services}} 10 | {{name}}: {{suffix}}, 11 | {{/services}} 12 | } 13 | 14 | #[routes] 15 | impl {{name}} { 16 | #[get("/")] 17 | fn index(&self) -> String { 18 | "Hello, world!".to_string() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sites/docs/src/theme/Layout/styles.module.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | } 5 | 6 | .mainWrapper { 7 | flex: 1 0 auto; 8 | display: flex; 9 | flex-direction: column; 10 | } 11 | 12 | /* Docusaurus-specific utility class */ 13 | :global(.docusaurus-mt-lg) { 14 | margin-top: 3rem; 15 | } 16 | 17 | :global(#__docusaurus) { 18 | min-height: 100%; 19 | display: flex; 20 | flex-direction: column; 21 | } 22 | -------------------------------------------------------------------------------- /starter-templates/with-vercel/api/[[...all]].rs: -------------------------------------------------------------------------------- 1 | use ngyn_vercel::VercelApplication; 2 | use vercel_runtime::{run, Body, Error, Request, Response}; 3 | 4 | #[tokio::main] 5 | async fn main() -> Result<(), Error> { 6 | run(handler).await 7 | } 8 | 9 | pub async fn handler(req: Request) -> Result, Error> { 10 | let app: VercelApplication = VercelApplication::default(); 11 | app.handle(req).await 12 | } 13 | -------------------------------------------------------------------------------- /crates/cli/src/templates/middleware.hbs: -------------------------------------------------------------------------------- 1 | use ngyn::prelude::*; 2 | 3 | {{#services}} 4 | use super::{{name}}::{{suffix}}; 5 | {{/services}} 6 | 7 | #[injectable] 8 | pub struct {{name}} { 9 | {{#services}} 10 | #[inject] 11 | pub {{name}}: {{suffix}}, 12 | {{/services}} 13 | } 14 | 15 | impl NgynMiddleware for {{name}} { 16 | fn handle(&self, cx: &mut NgynContext) ) { 17 | println!("{{name}} works!"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /starter-templates/with-vercel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "with-vercel" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "all" 8 | path = "api/[[...all]].rs" 9 | 10 | [dependencies] 11 | ngyn = { git = "https://github.com/ngyn-rs/ngyn", branch = "dev" } 12 | ngyn-vercel = { git = "https://github.com/ngyn-rs/ngyn", branch = "dev" } 13 | tokio = { version = "1", features = ["full"] } 14 | vercel_runtime = { version = "1.1.0" } 15 | -------------------------------------------------------------------------------- /starter-templates/with-vercel/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vercel Ngyn Starter 7 | 8 | 9 |

Welcome to the Vercel Ngyn Starter

10 | See a route served with Ngyn on the Vercel platform 11 | 12 | 13 | -------------------------------------------------------------------------------- /crates/ws/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. 3 | 4 | ## Unreleased 5 | #### Bug Fixes 6 | 7 | #### Features 8 | 9 | #### Miscellaneous Chores 10 | 11 | ## 0.1.2 - 2025-01-14 12 | 13 | ## 0.1.1 - 2024-12-21 14 | 15 | 16 | ## 0.1.0 - 2024-12-16 17 | #### Features 18 | - [#214](../../../../pull/214) **core**: websocket implementation 19 | -------------------------------------------------------------------------------- /cog.toml: -------------------------------------------------------------------------------- 1 | ignore_merge_commits = false 2 | 3 | [changelog] 4 | path = "CHANGELOG.md" 5 | authors = [ 6 | { signature = "Jonathan Irhodia", username = "elcharitas" } 7 | ] 8 | 9 | [packages] 10 | ngyn = { path = "crates/core" } 11 | cargo-ngyn = { path = "crates/cli" } 12 | ngyn_macros = { path = "crates/macros" } 13 | ngyn_shared = { path = "crates/shared" } 14 | ngyn-hyper = { path = "crates/hyper" } 15 | ngyn-vercel = { path = "crates/vercel" } 16 | ngyn-swagger = { path = "crates/swagger" } 17 | -------------------------------------------------------------------------------- /sites/docs/versioned_sidebars/version-0.4-sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "tutorialSidebar": [ 3 | { 4 | "type": "autogenerated", 5 | "dirName": "." 6 | }, 7 | { 8 | "type": "link", 9 | "label": "API Reference", 10 | "href": "https://docs.rs/ngyn" 11 | }, 12 | { 13 | "type": "link", 14 | "label": "Get Support", 15 | "href": "https://docs.rs/ngyn" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /crates/shared/src/server/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod body; 2 | pub mod context; 3 | pub mod response; 4 | pub mod transformer; 5 | 6 | pub use self::response::{JsonResponse, JsonResult}; 7 | pub use body::ToBytes; 8 | pub use bytes::Bytes; 9 | pub use context::NgynContext; 10 | pub use http::Method; 11 | use http_body_util::Full; 12 | pub use transformer::{Body, Param, Query, Transducer, Transformer}; 13 | 14 | pub type NgynRequest = http::Request>; 15 | pub type NgynResponse = http::Response>; 16 | -------------------------------------------------------------------------------- /examples/basic_app/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use ngyn::prelude::*; 4 | 5 | #[tokio::main] 6 | async fn main() { 7 | let mut app = HyperApplication::default(); 8 | 9 | app.get( 10 | "/author", 11 | handler(|_| Ok::<&str, ()>("Ngyn is created by @elcharitas.")), 12 | ); 13 | 14 | let _ = app.use_static(PathBuf::from("static")); 15 | 16 | println!("Starting server at http://127.0.0.1:8080"); 17 | 18 | let _ = app.listen("0.0.0.0:8080").await; 19 | } 20 | -------------------------------------------------------------------------------- /crates/cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. 3 | 4 | - - - 5 | ## ngyn_cli-0.3.0 - 2023-12-27 6 | #### Features 7 | - **(structure)** use crates to contain all crates - (647e4b7) - elcharitas 8 | #### Miscellaneous Chores 9 | - Rename to ngyn (#4) - (c81adc9) - elcharitas 10 | 11 | - - - 12 | 13 | Changelog generated by [cocogitto](https://github.com/cocogitto/cocogitto). -------------------------------------------------------------------------------- /crates/cli/src/data.rs: -------------------------------------------------------------------------------- 1 | // use anyhow::anyhow; 2 | // use anyhow::Result as AnyResult; 3 | use ramhorns::Content; 4 | 5 | pub struct CmdExit { 6 | pub code: exitcode::ExitCode, 7 | pub message: Option, 8 | } 9 | 10 | #[derive(Content)] 11 | pub struct Mods { 12 | pub name: String, 13 | pub suffix: String, 14 | } 15 | 16 | #[derive(Content)] 17 | pub struct Schematic { 18 | pub name: String, 19 | pub mods: Vec, 20 | pub services: Vec, 21 | pub initial: String, 22 | } 23 | -------------------------------------------------------------------------------- /sites/docs/versioned_sidebars/version-0.5.x-sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "tutorialSidebar": [ 3 | { 4 | "type": "autogenerated", 5 | "dirName": "." 6 | }, 7 | { 8 | "type": "link", 9 | "label": "API Reference", 10 | "href": "https://docs.rs/ngyn" 11 | }, 12 | { 13 | "type": "link", 14 | "label": "Get Support", 15 | "href": "https://cal.com/elcharitas/ngyn" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /sites/docs/src/theme/MDXComponents/Heading.tsx: -------------------------------------------------------------------------------- 1 | import { Heading } from "@site/src/components/Heading"; 2 | import type { Props } from "@theme/MDXComponents/Heading"; 3 | 4 | export default function MDXHeading({ as, ...props }: Props): JSX.Element { 5 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 6 | const _cls = "text-5xl text-4xl text-3xl text-2xl text-xl"; 7 | return ( 8 | // @ts-expect-error types 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /sites/docs/src/theme/CodeBlock/Container/index.tsx: -------------------------------------------------------------------------------- 1 | import Container from "@theme-original/CodeBlock/Container"; 2 | import type ContainerType from "@theme/CodeBlock/Container"; 3 | import type { WrapperProps } from "@docusaurus/types"; 4 | import CodeWrap from "@site/src/components/CodeWrap"; 5 | 6 | type Props = WrapperProps; 7 | 8 | export default function ContainerWrapper(props: Props): JSX.Element { 9 | return ( 10 | 11 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /examples/graphql_app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "graphql" 3 | version = "0.2.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hyper = { version = "1.0", features = ["full"] } 8 | juniper = { version = "0.16", features = ["expose-test-schema"] } 9 | juniper_hyper = "0.9" 10 | ngyn = { version = "0", path = "../../crates/core" } 11 | ngyn-hyper = { version = "0", path = "../../crates/hyper" } 12 | serde = { version = "1.0", features = ["derive"] } 13 | serde_json = "1.0" 14 | tokio = { version = "1.0", features = ["full"] } 15 | -------------------------------------------------------------------------------- /crates/macros/tests/core/param.expanded.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate ngyn_macros; 3 | use ngyn_macros::Param; 4 | struct UserParam { 5 | id: i32, 6 | name: String, 7 | } 8 | impl ngyn::shared::server::Transformer<'_> for UserParam { 9 | fn transform(cx: &mut ngyn::prelude::NgynContext<'_>) -> Self { 10 | let param = ngyn::shared::server::Param::transform(cx); 11 | UserParam { 12 | id: param.get("id").unwrap_or_default(), 13 | name: param.get("name").unwrap_or_default(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/vercel_app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vercel_app" 3 | version = "0.2.8" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "all" 8 | path = "api/[[...all]].rs" 9 | 10 | [dependencies] 11 | ngyn = { version = "0", path = "../../crates/core" } 12 | ngyn-vercel = { version = "0", path = "../../crates/vercel" } 13 | ngyn-swagger = { version = "0", path = "../../crates/swagger" } 14 | serde = { version = "1.0", features = ["derive"] } 15 | serde_json = "1.0" 16 | tokio = { version = "1.32.0", features = ["full"] } 17 | vercel_runtime = { version = "1.1.0" } 18 | -------------------------------------------------------------------------------- /examples/weather_app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "weather_api" 3 | version = "0.2.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | dotenv = "0.15.0" 10 | ngyn = { version = "0", path = "../../crates/core" } 11 | ngyn-shuttle = { version = "0", path = "../../crates/shuttle" } 12 | serde = { version = "1.0", features = ["derive"] } 13 | serde_json = "1.0" 14 | shuttle-runtime = "0" 15 | ureq = { version = "2.8.0" } 16 | validator = { version = "0.18.1", features = ["derive"] } 17 | -------------------------------------------------------------------------------- /crates/macros/tests/core/dto.expanded.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate ngyn_macros; 3 | use ngyn_macros::Dto; 4 | struct User { 5 | id: i32, 6 | name: String, 7 | } 8 | impl ngyn::shared::server::Transformer<'_> for User { 9 | fn transform(cx: &mut ngyn::prelude::NgynContext<'_>) -> Self { 10 | ngyn::prelude::Body::transform(cx).json::().unwrap() 11 | } 12 | } 13 | impl ngyn::shared::server::ToBytes for User { 14 | fn to_bytes(&self) -> ngyn::shared::server::Bytes { 15 | ngyn::shared::server::Bytes::from(serde_json::to_string(&self).unwrap()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/vercel_app/api/[[...all]].rs: -------------------------------------------------------------------------------- 1 | use ngyn_swagger::{NgynEngineSwagger, SwaggerConfig}; 2 | use ngyn_vercel::VercelApplication; 3 | use vercel_runtime::{run, Body, Error, Request, Response}; 4 | 5 | #[tokio::main] 6 | async fn main() -> Result<(), Error> { 7 | run(handler).await 8 | } 9 | 10 | pub async fn handler(req: Request) -> Result, Error> { 11 | let mut app = VercelApplication::default(); 12 | app.use_swagger(SwaggerConfig { 13 | spec_url: "/openapi.json".to_string(), 14 | ..Default::default() 15 | }); 16 | app.handle(req).await 17 | } 18 | -------------------------------------------------------------------------------- /crates/shuttle/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ngyn-shuttle" 3 | version = "0.2.3" 4 | edition = "2021" 5 | description = "Shuttle Runtime Platform for ngyn web framework" 6 | license = "MIT" 7 | documentation = "https://ngyn.rs/docs" 8 | repository = "https://github.com/ngyn-rs/ngyn" 9 | homepage = "https://ngyn.rs" 10 | rust-version = "1.81" 11 | keywords = ["ngyn", "run-time", "platform", "shuttle", "framework"] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | ngyn-hyper = { version = "0.2.3", path = "../../crates/hyper" } 17 | shuttle-runtime = "0" 18 | -------------------------------------------------------------------------------- /crates/macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ngyn_macros" 3 | version = "0.5.3" 4 | edition = "2021" 5 | description = "Modular backend framework for web applications" 6 | license = "MIT" 7 | documentation = "https://ngyn.rs/docs" 8 | repository = "https://github.com/ngyn-rs/ngyn" 9 | homepage = "https://ngyn.rs" 10 | rust-version = "1.81.0" 11 | 12 | [dependencies] 13 | http = { workspace = true } 14 | syn = { version = "2.0", features = ["full"] } 15 | quote = "1.0" 16 | ngyn_shared = { version = "0.5.3", path = "../shared" } 17 | 18 | [dev-dependencies] 19 | macrotest = "1" 20 | 21 | [lib] 22 | path = "src/lib.rs" 23 | proc-macro = true 24 | -------------------------------------------------------------------------------- /crates/ws/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ngyn-websocket" 3 | version = "0.1.2" 4 | edition = "2021" 5 | description = "Websocket Runtime Platform for ngyn web framework" 6 | license = "MIT" 7 | documentation = "https://ngyn.rs/docs" 8 | repository = "https://github.com/ngyn-rs/ngyn" 9 | homepage = "https://ngyn.rs" 10 | rust-version = "1.81" 11 | keywords = ["ngyn", "run-time", "platform", "websockets", "framework"] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | ngyn_shared = { version = "0.5.3", path = "../shared" } 17 | tokio = { version = "1", features = ["full"] } 18 | websocket = "0.27.1" 19 | -------------------------------------------------------------------------------- /crates/hyper/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ngyn-hyper" 3 | version = "0.2.3" 4 | edition = "2021" 5 | description = "Hyper Platform for ngyn web framework" 6 | license = "MIT" 7 | documentation = "https://ngyn.rs/docs" 8 | repository = "https://github.com/ngyn-rs/ngyn" 9 | homepage = "https://ngyn.rs" 10 | rust-version = "1.81.0" 11 | keywords = ["ngyn", "run-time", "platform", "hyper", "framework"] 12 | 13 | [dependencies] 14 | http-body-util = { workspace = true } 15 | hyper = { version = "1", features = ["full"] } 16 | hyper-util = { version = "0.1.10", features = ["full"] } 17 | ngyn_shared = { version = "0.5", path = "../shared" } 18 | tokio = { version = "1", features = ["full"] } 19 | -------------------------------------------------------------------------------- /crates/vercel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ngyn-vercel" 3 | version = "0.2.2" 4 | edition = "2021" 5 | description = "Vercel Runtime Platform for ngyn web framework" 6 | license = "MIT" 7 | documentation = "https://ngyn.rs/docs" 8 | repository = "https://github.com/ngyn-rs/ngyn" 9 | homepage = "https://ngyn.rs" 10 | rust-version = "1.81" 11 | keywords = ["ngyn", "run-time", "platform", "vercel", "framework"] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | ngyn_shared = { version = "0.5.3", path = "../shared" } 17 | tokio = { version = "1", features = ["full"] } 18 | vercel_runtime = { version = "1.1.4" } 19 | -------------------------------------------------------------------------------- /examples/weather_app/src/middlewares/notfound_middleware.rs: -------------------------------------------------------------------------------- 1 | use ngyn::{prelude::*, shared::server::ToBytes}; 2 | use serde_json::json; 3 | 4 | pub struct NotFoundMiddleware; 5 | 6 | impl NgynMiddleware for NotFoundMiddleware { 7 | async fn handle(cx: &mut NgynContext<'_>) { 8 | if cx.params().is_none() { 9 | let body = json!({ 10 | "error": { 11 | "status": 404, // this will be interpreted by the ResponseInterpreter, and set as the status code 12 | "message": "Route not found", 13 | } 14 | }); 15 | *cx.response_mut().body_mut() = body.to_bytes().into(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /crates/swagger/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ngyn-swagger" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "Shuttle Runtime Platform for ngyn web framework" 6 | license = "MIT" 7 | documentation = "https://ngyn.rs/docs" 8 | repository = "https://github.com/ngyn-rs/ngyn" 9 | homepage = "https://ngyn.rs" 10 | rust-version = "1.81" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | ngyn = { version = "0.5", path = "../core" } 16 | ngyn-swagger-macros = { version = "0.1", path = "../swagger-macros" } 17 | serde = { version = "1.0", features = ["derive"] } 18 | serde_json = "1.0" 19 | 20 | [lib] 21 | path = "src/lib.rs" 22 | -------------------------------------------------------------------------------- /examples/weather_app/src/main.rs: -------------------------------------------------------------------------------- 1 | mod middlewares; 2 | mod weather; 3 | 4 | use dotenv::dotenv; 5 | use ngyn::prelude::HyperConfig; 6 | use ngyn::prelude::*; 7 | use ngyn_shuttle::{ShuttleApplication, ShuttleNgyn}; 8 | use weather::{get_location, post_location}; 9 | 10 | use crate::middlewares::notfound_middleware::NotFoundMiddleware; 11 | 12 | #[shuttle_runtime::main] 13 | async fn main() -> ShuttleNgyn { 14 | dotenv().ok(); 15 | let mut app = ShuttleApplication::with_config(HyperConfig::default()); 16 | 17 | app.get("/{location}/{city}", async_wrap(get_location)); 18 | app.any("/", async_wrap(post_location)); 19 | 20 | app.use_middleware(NotFoundMiddleware {}); 21 | 22 | Ok(app.into()) 23 | } 24 | -------------------------------------------------------------------------------- /crates/vercel/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. 3 | 4 | ## Unreleased 5 | #### Bug Fixes 6 | 7 | #### Features 8 | 9 | #### Miscellaneous Chores 10 | 11 | ## 0.2.2 - 2025-01-14 12 | 13 | ## 0.2.1 - 2024-12-21 14 | 15 | ## 0.2.0 - 2024-12-16 16 | #### Features 17 | - [#217](../../../../pull/217) **core**: update ngyn version to v0.5 18 | 19 | ## 0.1.1 - 2024-09-07 20 | #### Features 21 | - [#186](../../pull/186) **(chore)**: add support for bytes reading 22 | 23 | 24 | ## 0.1.0 - 2024-07-05 25 | #### Features 26 | - [#105](../../pull/105) **(platform)**: add support for `vercel` platform 27 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/foundations/modules.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Modules 6 | 7 | Modules are a way to organize your application into reusable components. They are similar to modules in other frameworks and can be used to group related components together. 8 | 9 | ## Defining Modules 10 | 11 | To define a module, you need to implement the `NgynModule` trait for the component. Ideally, you should never have to implement the `NgynModule` trait yourself. Instead, you should use the `#[module]` attribute macro to define your modules. This macro will automatically implement the `NgynModule` trait for you. 12 | 13 | ```rust 14 | use ngyn::prelude::*; 15 | 16 | #[module] 17 | struct AppModule; 18 | ``` 19 | -------------------------------------------------------------------------------- /crates/swagger-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ngyn-swagger-macros" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "Shuttle Runtime Platform for ngyn web framework" 6 | license = "MIT" 7 | documentation = "https://ngyn.rs/docs" 8 | repository = "https://github.com/ngyn-rs/ngyn" 9 | homepage = "https://ngyn.rs" 10 | rust-version = "1.81" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | ngyn-hyper = { version = "0", path = "../../crates/hyper" } 16 | quote = "1.0" 17 | serde = { version = "1.0", features = ["derive"] } 18 | serde_json = "1.0" 19 | syn = { version = "2.0", features = ["full"] } 20 | 21 | [lib] 22 | path = "src/lib.rs" 23 | proc-macro = true 24 | -------------------------------------------------------------------------------- /crates/core/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | 3 | pub mod macros { 4 | pub use ngyn_macros::*; 5 | } 6 | 7 | pub mod shared { 8 | pub use ngyn_shared::*; 9 | } 10 | 11 | #[doc(hidden)] 12 | pub mod prelude { 13 | pub use crate::macros::*; 14 | pub use ngyn_hyper::{HyperApplication, HyperConfig}; 15 | pub use ngyn_shared::{ 16 | core::{ 17 | engine::{NgynEngine, NgynHttpEngine}, 18 | handler::*, 19 | }, 20 | server::{ 21 | Body, JsonResponse, JsonResult, NgynContext, NgynRequest, NgynResponse, Param, Query, 22 | ToBytes, Transducer, 23 | }, 24 | NgynGate, NgynMiddleware, 25 | }; 26 | } 27 | 28 | pub mod http { 29 | pub use http::*; 30 | } 31 | -------------------------------------------------------------------------------- /sites/docs/src/components/Prose.tsx: -------------------------------------------------------------------------------- 1 | import clsx from "clsx"; 2 | 3 | export function Prose({ 4 | as, 5 | className, 6 | ...props 7 | }: Omit, "as" | "className"> & { 8 | as?: T; 9 | className?: string; 10 | }) { 11 | const Component = as ?? "div"; 12 | 13 | return ( 14 | *)` is used to select all direct children without an increase in specificity like you'd get from just `& > *` 19 | "[html_:where(&>*)]:mx-auto [html_:where(&>*)]:max-w-2xl [html_:where(&>*)]:lg:mx-[calc(50%-min(50%,theme(maxWidth.lg)))] [html_:where(&>*)]:lg:max-w-3xl", 20 | )} 21 | {...props} 22 | /> 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /crates/shuttle/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. 3 | 4 | ## Unreleased 5 | #### Bug Fixes 6 | 7 | #### Features 8 | 9 | #### Miscellaneous Chores 10 | 11 | ## 0.2.3 - 2025-01-29 12 | #### Bug Fixes 13 | - [#252](../../../../pull/252) **platform**: broken hyper graceful shutdown 14 | 15 | ## 0.2.2 - 2025-01-14 16 | 17 | ## 0.2.1 - 2024-12-21 18 | 19 | 20 | ## 0.2.0 - 2024-12-16 21 | #### Features 22 | - [#217](../../../../pull/217) **core**: update ngyn version to v0.5 23 | 24 | ## 0.1.1 - 2024-09-07 25 | #### Bug Fixes 26 | 27 | 28 | ## 0.1.0 - 2024-07-05 29 | #### Features 30 | - [#105](../../pull/105) **(platform)**: add support for `shuttle` platform 31 | -------------------------------------------------------------------------------- /crates/macros/tests/common/state.expanded.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate ngyn_macros; 3 | use ngyn_macros::AppState; 4 | struct TestState { 5 | name: String, 6 | } 7 | impl ngyn::shared::server::context::AppState for TestState { 8 | fn as_any(&self) -> &dyn std::any::Any { 9 | self 10 | } 11 | fn as_any_mut(&mut self) -> &mut dyn std::any::Any { 12 | self 13 | } 14 | } 15 | impl<'a> ngyn::shared::server::Transformer<'a> for &'a TestState { 16 | fn transform(cx: &'a mut ngyn::prelude::NgynContext<'_>) -> Self { 17 | cx.state::().unwrap() 18 | } 19 | } 20 | impl<'a> ngyn::shared::server::Transformer<'a> for &'a mut TestState { 21 | fn transform(cx: &'a mut ngyn::prelude::NgynContext<'_>) -> Self { 22 | cx.state_mut::().unwrap() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sites/docs/src/theme/Layout/Provider/index.tsx: -------------------------------------------------------------------------------- 1 | import { composeProviders } from "@docusaurus/theme-common"; 2 | import { 3 | ColorModeProvider, 4 | AnnouncementBarProvider, 5 | ScrollControllerProvider, 6 | NavbarProvider, 7 | PluginHtmlClassNameProvider, 8 | } from "@docusaurus/theme-common/internal"; 9 | import { DocsPreferredVersionContextProvider } from "@docusaurus/plugin-content-docs/client"; 10 | import type { Props } from "@theme/Layout/Provider"; 11 | 12 | const Provider = composeProviders([ 13 | ColorModeProvider, 14 | AnnouncementBarProvider, 15 | ScrollControllerProvider, 16 | DocsPreferredVersionContextProvider, 17 | PluginHtmlClassNameProvider, 18 | NavbarProvider, 19 | ]); 20 | 21 | export default function LayoutProvider({ children }: Props): JSX.Element { 22 | return {children}; 23 | } 24 | -------------------------------------------------------------------------------- /examples/sqlx_app/.sqlx/query-843923b9a0257cf80f1dff554e7dc8fdfc05f489328e8376513124dfb42996e3.json: -------------------------------------------------------------------------------- 1 | { 2 | "db_name": "PostgreSQL", 3 | "query": "SELECT * FROM users WHERE id = $1", 4 | "describe": { 5 | "columns": [ 6 | { 7 | "ordinal": 0, 8 | "name": "id", 9 | "type_info": "Int4" 10 | }, 11 | { 12 | "ordinal": 1, 13 | "name": "name", 14 | "type_info": "Text" 15 | }, 16 | { 17 | "ordinal": 2, 18 | "name": "created_at", 19 | "type_info": "Timestamptz" 20 | } 21 | ], 22 | "parameters": { 23 | "Left": [ 24 | "Int4" 25 | ] 26 | }, 27 | "nullable": [ 28 | false, 29 | true, 30 | true 31 | ] 32 | }, 33 | "hash": "843923b9a0257cf80f1dff554e7dc8fdfc05f489328e8376513124dfb42996e3" 34 | } 35 | -------------------------------------------------------------------------------- /crates/cli/src/bin/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unnecessary_wraps)] 2 | #![allow(clippy::missing_const_for_fn)] 3 | 4 | mod cmd; 5 | 6 | fn main() { 7 | let mut app = cmd::default::command() 8 | .subcommand(cmd::generate::command()) 9 | .subcommand(cmd::new::command()); 10 | 11 | // let v = app.render_version(); 12 | let matches = app.clone().get_matches(); 13 | 14 | // use info! or trace! etc. to log 15 | cmd::tracing(&matches); 16 | 17 | let res = matches.subcommand().map_or_else( 18 | || cmd::default::run(&mut app, &matches), 19 | |tup| match tup { 20 | ("generate", subcommand_matches) => cmd::generate::run(&matches, subcommand_matches), 21 | ("new", subcommand_matches) => cmd::new::run(&matches, subcommand_matches), 22 | _ => unreachable!(), 23 | }, 24 | ); 25 | 26 | cmd::result_exit(res); 27 | } 28 | -------------------------------------------------------------------------------- /crates/shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ngyn_shared" 3 | version = "0.5.3" 4 | edition = "2021" 5 | description = "Modular backend framework for web applications" 6 | license = "MIT" 7 | documentation = "https://ngyn.rs/docs" 8 | repository = "https://github.com/ngyn-rs/ngyn" 9 | homepage = "https://ngyn.rs" 10 | rust-version = "1.81.0" 11 | keywords = ["ngyn", "shared", "platform", "framework"] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | bytes = { workspace = true } 17 | futures-util = { version = "0.3", default-features = false } 18 | http-body-util = { workspace = true } 19 | http = { workspace = true } 20 | matchit = "0.8.5" 21 | multer = "3.1.0" 22 | serde = { workspace = true } 23 | serde_json = { workspace = true } 24 | url = "2.5.0" 25 | 26 | [dev-dependencies] 27 | tokio = { version = "1", features = ["rt-multi-thread", "macros"] } 28 | -------------------------------------------------------------------------------- /sites/docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /crates/shuttle/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ngyn_hyper::HyperApplication; 2 | use shuttle_runtime::{CustomError, Error}; 3 | use std::net::SocketAddr; 4 | 5 | pub type ShuttleApplication = HyperApplication; 6 | pub struct NgynService(HyperApplication); 7 | 8 | #[shuttle_runtime::async_trait] 9 | impl shuttle_runtime::Service for NgynService { 10 | /// Takes the app that is returned by the user in their [shuttle_runtime::main] function 11 | /// and binds to an address passed in by shuttle. 12 | async fn bind(mut self, addr: SocketAddr) -> Result<(), Error> { 13 | let _ = self 14 | .0 15 | .listen(addr) 16 | .await 17 | .map_err(|err| CustomError::new::(err.into()))?; 18 | Ok(()) 19 | } 20 | } 21 | 22 | impl From for NgynService { 23 | fn from(app: HyperApplication) -> Self { 24 | Self(app) 25 | } 26 | } 27 | 28 | pub type ShuttleNgyn = Result; 29 | -------------------------------------------------------------------------------- /crates/hyper/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. 3 | 4 | ## Unreleased 5 | #### Bug Fixes 6 | 7 | #### Features 8 | 9 | #### Miscellaneous Chores 10 | 11 | ## 0.2.3 - 2025-01-29 12 | #### Bug Fixes 13 | - [#252](../../../../pull/252) **platform**: broken hyper graceful shutdown 14 | 15 | ## 0.2.2 - 2025-01-14 16 | #### Features 17 | - [#247](../../../../pull/247) **core**: http1 application configuration 18 | 19 | ## 0.2.1 - 2024-12-21 20 | #### Bug Fixes 21 | - [#223](../../../../pull/223) **core**: implement graceful shutdown on `hyper` platform 22 | 23 | ## 0.2.0 - 2024-12-16 24 | #### Features 25 | - [#217](../../../../pull/217) **core**: update ngyn version to v0.5 26 | 27 | ## 0.1.1 - 2024-09-07 28 | #### Bug Fixes 29 | 30 | 31 | ## 0.1.0 - 2024-07-05 32 | #### Features 33 | - [#105](../../../../pull/105) **(platform)**: add support for `hyper` platform 34 | -------------------------------------------------------------------------------- /crates/vercel/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ngyn_shared::{ 2 | core::engine::{NgynHttpPlatform, PlatformData}, 3 | server::response::ReadBytes, 4 | }; 5 | use vercel_runtime::{Body, Error, Request, Response as VercelResponse}; 6 | 7 | #[derive(Default)] 8 | pub struct VercelApplication { 9 | data: PlatformData, 10 | } 11 | 12 | impl NgynHttpPlatform for VercelApplication { 13 | fn data_mut(&mut self) -> &mut PlatformData { 14 | &mut self.data 15 | } 16 | } 17 | 18 | impl VercelApplication { 19 | pub async fn handle(self, request: Request) -> Result, Error> { 20 | let request = request.map(|b| b.to_vec()); 21 | let mut response = self.data.respond(request).await; 22 | 23 | let body = response 24 | .read_bytes() 25 | .await 26 | .expect("Response hasn't been set"); 27 | 28 | let (parts, ..) = response.into_parts(); 29 | 30 | Ok(VercelResponse::from_parts(parts, Body::from(body.to_vec()))) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /crates/macros/src/core/dto.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | 4 | pub(crate) fn dto_macro(input: TokenStream) -> TokenStream { 5 | let syn::ItemStruct { 6 | ident, generics, .. 7 | } = syn::parse_macro_input!(input as syn::ItemStruct); 8 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 9 | 10 | let expanded = quote! { 11 | impl #impl_generics ngyn::shared::server::Transformer<'_> for #ident #ty_generics #where_clause { 12 | fn transform(cx: &mut ngyn::prelude::NgynContext<'_>) -> Self { 13 | ngyn::prelude::Body::transform(cx).json::<#ident>().unwrap() 14 | } 15 | } 16 | 17 | impl #impl_generics ngyn::shared::server::ToBytes for #ident #ty_generics #where_clause { 18 | fn to_bytes(&self) -> ngyn::shared::server::Bytes { 19 | ngyn::shared::server::Bytes::from(serde_json::to_string(&self).unwrap()) 20 | } 21 | } 22 | }; 23 | expanded.into() 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Rust 2 | 3 | todo.txt 4 | dist/ 5 | 6 | # will have compiled files and executables 7 | debug/ 8 | build/ 9 | coverage/ 10 | 11 | # Add Cargo.lock to gitignore if creating a library 12 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 13 | # Cargo.lock 14 | 15 | # These are backup files generated by rustfmt 16 | **/*.rs.bk 17 | 18 | # MSVC Windows builds of rustc generate these, which store debugging information 19 | *.pdb 20 | 21 | 22 | ## Node 23 | 24 | node_modules/ 25 | 26 | ## General 27 | 28 | .DS_Store 29 | .AppleDouble 30 | .LSOverride 31 | 32 | # Icon must end with two \r 33 | Icon 34 | 35 | # Thumbnails 36 | ._* 37 | 38 | # Files that might appear in the root of a volume 39 | .DocumentRevisions-V100 40 | .fseventsd 41 | .Spotlight-V100 42 | .TemporaryItems 43 | .Trashes 44 | .VolumeIcon.icns 45 | .com.apple.timemachine.donotpresent 46 | .idea 47 | 48 | # Directories potentially created on remote AFP share 49 | .AppleDB 50 | .AppleDesktop 51 | Network Trash Folder 52 | Temporary Items 53 | .apdisk 54 | -------------------------------------------------------------------------------- /rustwrap.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | - platform: win32 3 | arch: x64 4 | url_template: https://github.com/ngyn-rs/ngyn/releases/download/v__VERSION__/ngyn-x86_64-windows.zip 5 | - platform: linux 6 | arch: x64 7 | url_template: https://github.com/ngyn-rs/ngyn/releases/download/v__VERSION__/ngyn-x86_64-linux.tar.xz 8 | - platform: darwin 9 | arch: x64 10 | url_template: https://github.com/ngyn-rs/ngyn/releases/download/v__VERSION__/ngyn-x86_64-macos.tar.xz 11 | - platform: darwin 12 | arch: arm64 13 | url_template: https://github.com/ngyn-rs/ngyn/releases/download/v__VERSION__/ngyn-aarch64-macos.tar.xz 14 | 15 | brew: 16 | name: ngyn 17 | publish: true 18 | tap: ngyn 19 | recipe_fname: ngyn.rb 20 | recipe_template: | 21 | class NgynFormula < Formula 22 | desc "A progressive backend framework for Rust" 23 | homepage "https://github.com/ngyn-rs/ngyn" 24 | url "__URL__" 25 | version "__VERSION__" 26 | sha256 "__SHA__" 27 | 28 | def install 29 | bin.install "ngyn" 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /crates/core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ngyn" 3 | version = "0.5.4" 4 | edition = "2021" 5 | description = "Modular backend framework for web applications" 6 | license = "MIT" 7 | rust-version = "1.81.0" 8 | documentation = "https://ngyn.rs/docs" 9 | repository = "https://github.com/ngyn-rs/ngyn" 10 | homepage = "https://ngyn.rs" 11 | keywords = [ 12 | "web-apps", 13 | "web-server", 14 | "web-framework", 15 | "back-end", 16 | "applications", 17 | ] 18 | 19 | [lib] 20 | path = "src/lib.rs" 21 | 22 | [dependencies] 23 | http = { workspace = true } 24 | ngyn_macros = { version = "0.5.3", path = "../macros", optional = true } 25 | ngyn_shared = { version = "0.5.3", path = "../shared" } 26 | ngyn-hyper = { version = "0.2.3", path = "../hyper", optional = true } 27 | ngyn-websocket = { version = "0.1.2", path = "../ws", optional = true } 28 | ngyn-vercel = { version = "0.2.2", path = "../vercel", optional = true } 29 | 30 | [features] 31 | default = ["hyper"] 32 | hyper = ["ngyn-hyper", "ngyn_macros"] 33 | macros = ["ngyn_macros"] 34 | websocket = ["ngyn-websocket"] 35 | vercel = ["ngyn-vercel", "ngyn_macros"] 36 | -------------------------------------------------------------------------------- /examples/sqlx_app/src/main.rs: -------------------------------------------------------------------------------- 1 | use ngyn::prelude::*; 2 | use sqlx::{Connection, PgConnection}; 3 | 4 | #[derive(AppState)] 5 | struct State { 6 | conn: PgConnection, 7 | } 8 | 9 | #[derive(Param)] 10 | struct HandleParam { 11 | id: i32, 12 | } 13 | 14 | #[handler] 15 | async fn handle_get(param: HandleParam, state: &mut State) -> String { 16 | match sqlx::query!("SELECT * FROM users WHERE id = $1", param.id) 17 | .fetch_one(&mut state.conn) 18 | .await 19 | { 20 | Ok(record) => record.name.unwrap(), 21 | Err(_) => "Not found".to_string(), 22 | } 23 | } 24 | 25 | #[tokio::main] 26 | async fn main() { 27 | dotenv::dotenv().ok(); 28 | let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"); 29 | let mut app = HyperApplication::default(); 30 | let conn = PgConnection::connect(&database_url).await.unwrap(); 31 | 32 | app.set_state(State { conn }); 33 | 34 | app.get("/{id}", async_wrap(handle_get)); 35 | 36 | println!("Starting server at http://127.0.0.1:8080"); 37 | 38 | let _ = app.listen("0.0.0.0:8080").await; 39 | } 40 | -------------------------------------------------------------------------------- /crates/macros/src/common/service.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | 4 | pub(crate) fn service_macro(input: TokenStream) -> TokenStream { 5 | let syn::ItemStruct { 6 | ident, 7 | generics, 8 | fields, 9 | .. 10 | } = syn::parse_macro_input!(input as syn::ItemStruct); 11 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 12 | 13 | let fields: Vec<_> = fields 14 | .iter() 15 | .map( 16 | |syn::Field { 17 | ident, colon_token, .. 18 | }| { 19 | quote! { 20 | #ident #colon_token Default::default() 21 | } 22 | }, 23 | ) 24 | .collect(); 25 | 26 | let expanded = quote! { 27 | impl #impl_generics ngyn::shared::server::Transformer<'_> for #ident #ty_generics #where_clause { 28 | fn transform(cx: &mut ngyn::prelude::NgynContext<'_>) -> Self { 29 | Self { 30 | #(#fields)* 31 | } 32 | } 33 | } 34 | }; 35 | expanded.into() 36 | } 37 | -------------------------------------------------------------------------------- /sites/docs/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | import tseslint from "typescript-eslint"; 4 | import rawPluginReactConfig from "eslint-plugin-react/configs/recommended.js"; 5 | import { fixupConfigRules } from "@eslint/compat"; 6 | 7 | const pluginReactConfig = { 8 | ...rawPluginReactConfig, 9 | rules: { 10 | ...rawPluginReactConfig.rules, 11 | "react/react-in-jsx-scope": 0, 12 | }, 13 | }; 14 | 15 | const tsConfigs = tseslint.configs.recommended.map((c) => { 16 | if (c.rules && "@typescript-eslint/no-var-requires" in c.rules) { 17 | c.rules["@typescript-eslint/no-var-requires"] = 0; 18 | } 19 | return c; 20 | }); 21 | 22 | export default [ 23 | { 24 | ignores: [".docusaurus/*", "tailwind.config.js", "babel.config.js"], 25 | }, 26 | { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] }, 27 | { languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } }, 28 | { languageOptions: { globals: globals.browser } }, 29 | pluginJs.configs.recommended, 30 | ...tsConfigs, 31 | ...fixupConfigRules(pluginReactConfig), 32 | ]; 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Jonathan Irhodia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /crates/macros/src/core/param.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | use syn::Field; 4 | 5 | pub(crate) fn param_macro(input: TokenStream) -> TokenStream { 6 | let syn::ItemStruct { 7 | ident, 8 | generics, 9 | fields, 10 | .. 11 | } = syn::parse_macro_input!(input as syn::ItemStruct); 12 | 13 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 14 | let fields: Vec<_> = fields 15 | .iter() 16 | .map(|Field { ident, .. }| { 17 | quote! { 18 | #ident: param.get(stringify!(#ident)).unwrap_or_default(), 19 | } 20 | }) 21 | .collect(); 22 | 23 | let expanded = quote! { 24 | impl #impl_generics ngyn::shared::server::Transformer<'_> for #ident #ty_generics #where_clause { 25 | fn transform(cx: &mut ngyn::prelude::NgynContext<'_>) -> Self { 26 | let param = ngyn::shared::server::Param::transform(cx); 27 | #ident { 28 | #(#fields)* 29 | } 30 | } 31 | } 32 | }; 33 | 34 | expanded.into() 35 | } 36 | -------------------------------------------------------------------------------- /crates/macros/src/core/query.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | use syn::Field; 4 | 5 | pub(crate) fn query_macro(input: TokenStream) -> TokenStream { 6 | let syn::ItemStruct { 7 | ident, 8 | generics, 9 | fields, 10 | .. 11 | } = syn::parse_macro_input!(input as syn::ItemStruct); 12 | 13 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 14 | let fields: Vec<_> = fields 15 | .iter() 16 | .map(|Field { ident, .. }| { 17 | quote! { 18 | #ident: query.get(stringify!(#ident)).unwrap_or_default(), 19 | } 20 | }) 21 | .collect(); 22 | 23 | let expanded = quote! { 24 | impl #impl_generics ngyn::shared::server::Transformer<'_> for #ident #ty_generics #where_clause { 25 | fn transform(cx: &mut ngyn::prelude::NgynContext<'_>) -> Self { 26 | let query = ngyn::shared::server::Query::transform(cx); 27 | #ident { 28 | #(#fields)* 29 | } 30 | } 31 | } 32 | }; 33 | 34 | expanded.into() 35 | } 36 | -------------------------------------------------------------------------------- /crates/shared/src/core/statics.rs: -------------------------------------------------------------------------------- 1 | { 2 | let mut current_dir = std::env::current_dir().expect("Current working directory could not be read"); 3 | current_dir.push(path_buf); 4 | let entries = std::fs::read_dir(current_dir.clone()).expect("Specified static path does not exist"); 5 | let str_current_dir = current_dir.to_str().unwrap(); 6 | 7 | let mut assets: std::collections::HashMap = std::collections::HashMap::new(); 8 | 9 | for entry in entries { 10 | let path = entry?.path(); 11 | 12 | if path.is_file() { 13 | let file_path = path 14 | .to_str() 15 | .expect("file name contains invalid unicode characters"); 16 | 17 | let content = std::fs::read(file_path).unwrap(); // Infallible, would always exist 18 | 19 | // Convert file data into a static slice 20 | let static_content: &'static [u8] = Box::leak(content.into_boxed_slice()); 21 | 22 | assets.insert(file_path.replace(str_current_dir, ""), static_content); 23 | } else if path.is_dir() { 24 | self.use_static(path)?; 25 | } 26 | } 27 | 28 | assets 29 | } -------------------------------------------------------------------------------- /crates/cli/src/bin/cmd/default.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use clap::{crate_version, Arg, ArgAction}; 3 | use clap::{ArgMatches, Command}; 4 | use tracing::info; 5 | 6 | pub fn command() -> Command { 7 | Command::new("ngyn ") 8 | .version(crate_version!()) 9 | .about("A powerful and flexible web application framework for Rust.") 10 | .arg( 11 | Arg::new("verbose") 12 | .long("verbose") 13 | .help("Show details about interactions") 14 | .action(ArgAction::SetTrue), 15 | ) 16 | .after_help("For more information on a specific command, run 'ngyn help [SUBCOMMAND]' or 'ngyn [SUBCOMMAND] --help'.\n\nDocumentation: https://ngyn.rs/docs\nReport bugs: https://github.com/ngyn-rs/ngyn/issues") 17 | } 18 | 19 | pub fn run(cmd: &mut Command, matches: &ArgMatches) -> Result { 20 | info!("default cmd {:?}", matches.get_one::("reporter")); 21 | let version = crate_version!(); 22 | 23 | println!("ngyn {}", version); 24 | 25 | cmd.print_long_help().unwrap(); 26 | 27 | Ok(cargo_ngyn::CmdExit { 28 | code: exitcode::OK, 29 | message: None, 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | new_version=$1 4 | 5 | # Install smart-release if it's not already installed 6 | cargo install smart-release 7 | 8 | root_dir=$(git rev-parse --show-toplevel) 9 | 10 | # Find all subfolders in the crates directory 11 | crate_names=("shared" "macros" "hyper" "vercel" "shuttle") 12 | examples_dirs=() 13 | 14 | while IFS= read -r -d $'\0' example_dir; do 15 | if [[ $example_dir != "examples" ]]; then 16 | echo "Found example: $example_dir" 17 | examples_dirs+=("$example_dir") 18 | fi 19 | done < <(find examples -maxdepth 1 -type d -print0) 20 | 21 | # bump core version used in the examples 22 | for example_dir in "${examples_dirs[@]}"; do 23 | echo "Bumping version in $example_dir" 24 | sed -i '' -e "s/^ngyn = .*/ngyn = \"$new_version\"/" $example_dir/Cargo.toml 25 | done 26 | 27 | # publish the version of all crates 28 | for crate_name in "${crate_names[@]}"; do 29 | echo "Publishing version $new_version of $crate_name" 30 | cargo smart-release --execute --no-changelog -b keep -d keep $crate_name 31 | # sleep for two minutes to avoid rate limiting 32 | sleep 120 # 2 minutes 33 | done 34 | 35 | cd $root_dir/crates/core 36 | # publish the version of the core crate 37 | echo "Publishing version $new_version of ngyn" 38 | cargo publish 39 | -------------------------------------------------------------------------------- /crates/swagger/src/templates/swagger.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | % SWAGGER_DOC_TITLE % 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /sites/docs/src/components/GridPattern.tsx: -------------------------------------------------------------------------------- 1 | import { useId } from "react"; 2 | 3 | export function GridPattern({ 4 | width, 5 | height, 6 | x, 7 | y, 8 | squares, 9 | ...props 10 | }: React.ComponentPropsWithoutRef<"svg"> & { 11 | width: number; 12 | height: number; 13 | x: string | number; 14 | y: string | number; 15 | squares: Array<[x: number, y: number]>; 16 | }) { 17 | const patternId = useId(); 18 | 19 | return ( 20 | 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /crates/core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. 3 | 4 | ## Unreleased 5 | #### Bug Fixes 6 | 7 | #### Features 8 | 9 | #### Miscellaneous Chores 10 | 11 | ## 0.5.4 - 2025-01-29 12 | #### Bug Fixes 13 | - [#252](../../../../pull/252) **platform**: broken hyper graceful shutdown 14 | 15 | ## 0.5.3 - 2025-01-14 16 | 17 | ## 0.5.2 - 2024-12-30 18 | 19 | ## 0.5.1 - 2024-12-21 20 | 21 | ## 0.5.0 - 2024-12-16 22 | #### Bug Fixes 23 | - [#216](../../../../pull/216) **routing**: unwrap grouped routes 24 | 25 | #### Features 26 | - [#192](../../../../pull/192) **routing**: add support for async handlers 27 | - [#205](../../../../pull/205) **routing**: drop support for controlled routing 28 | - [#214](../../../../pull/214) **platforms**: websockets implementation 29 | - [#215](../../../../pull/215) **routing**: grouped routes and router 30 | 31 | #### Miscellaneous Chores 32 | - [#195](../../../../pull/195) **core**: add info to explain how arc to box works 33 | - [#203](../../../../pull/203) **routing**: add reexports for handlers 34 | - [#213](../../../../pull/213) **core**: `ToBytes` implementation + Crates keywords 35 | - [#207](../../../../pull/207) **core**: cleanup interpreter and async traits -------------------------------------------------------------------------------- /crates/macros/src/common/state.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | 3 | pub(crate) fn derive_app_state_macro(input: TokenStream) -> TokenStream { 4 | let syn::ItemStruct { 5 | ident, generics, .. 6 | } = syn::parse_macro_input!(input as syn::ItemStruct); 7 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 8 | 9 | let expanded = quote::quote! { 10 | impl #impl_generics ngyn::shared::server::context::AppState for #ident #ty_generics #where_clause { 11 | fn as_any(&self) -> &dyn std::any::Any { 12 | self 13 | } 14 | fn as_any_mut(&mut self) -> &mut dyn std::any::Any { 15 | self 16 | } 17 | } 18 | 19 | impl<'a> #impl_generics ngyn::shared::server::Transformer<'a> for &'a #ident #ty_generics #where_clause { 20 | fn transform(cx: &'a mut ngyn::prelude::NgynContext<'_>) -> Self { 21 | cx.state::<#ident>().unwrap() 22 | } 23 | } 24 | impl<'a> #impl_generics ngyn::shared::server::Transformer<'a> for &'a mut #ident #ty_generics #where_clause { 25 | fn transform(cx: &'a mut ngyn::prelude::NgynContext<'_>) -> Self { 26 | cx.state_mut::<#ident>().unwrap() 27 | } 28 | } 29 | }; 30 | TokenStream::from(expanded) 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release Workflow 2 | on: 3 | push: 4 | branches: [main] 5 | workflow_dispatch: 6 | inputs: 7 | rustc_version: 8 | description: "Rustc version" 9 | required: true 10 | default: "1.81.0" 11 | 12 | jobs: 13 | crates_io_publish: 14 | name: Publish (crates.io) 15 | runs-on: ubuntu-latest 16 | timeout-minutes: 25 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: dtolnay/rust-toolchain@master 20 | with: 21 | toolchain: ${{ github.event.inputs.rustc_version || '1.81.0' }} 22 | 23 | - name: cargo-release Cache 24 | id: cargo_release_cache 25 | uses: actions/cache@v3 26 | with: 27 | path: ~/.cargo/bin/cargo-release 28 | key: ${{ runner.os }}-cargo-release 29 | 30 | - run: cargo install cargo-release cargo-expand 31 | if: steps.cargo_release_cache.outputs.cache-hit != 'true' 32 | 33 | - name: cargo login 34 | run: cargo login ${{ secrets.CRATES_IO_API_TOKEN }} 35 | 36 | - name: "cargo release publish" 37 | run: |- 38 | cargo release \ 39 | publish \ 40 | --manifest-path ./crates/Cargo.toml \ 41 | --workspace \ 42 | --all-features \ 43 | --allow-branch main \ 44 | --no-confirm \ 45 | --no-verify \ 46 | --execute 47 | -------------------------------------------------------------------------------- /sites/docs/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import headlessuiPlugin from "@headlessui/tailwindcss"; 2 | import typographyPlugin from "@tailwindcss/typography"; 3 | import type { Config } from "tailwindcss"; 4 | 5 | import typographyStyles from "./typography"; 6 | 7 | export default { 8 | content: ["./src/**/*.{js,mjs,jsx,ts,tsx,mdx}"], 9 | darkMode: "selector", 10 | theme: { 11 | fontSize: { 12 | "2xs": ["0.75rem", { lineHeight: "1.25rem" }], 13 | xs: ["0.8125rem", { lineHeight: "1.5rem" }], 14 | sm: ["0.875rem", { lineHeight: "1.5rem" }], 15 | base: ["1rem", { lineHeight: "1.75rem" }], 16 | lg: ["1.125rem", { lineHeight: "1.75rem" }], 17 | xl: ["1.25rem", { lineHeight: "1.75rem" }], 18 | "2xl": ["1.5rem", { lineHeight: "2rem" }], 19 | "3xl": ["1.875rem", { lineHeight: "2.25rem" }], 20 | "4xl": ["2.25rem", { lineHeight: "2.5rem" }], 21 | "5xl": ["3rem", { lineHeight: "1" }], 22 | "6xl": ["3.75rem", { lineHeight: "1" }], 23 | "7xl": ["4.5rem", { lineHeight: "1" }], 24 | "8xl": ["6rem", { lineHeight: "1" }], 25 | "9xl": ["8rem", { lineHeight: "1" }], 26 | }, 27 | typography: typographyStyles, 28 | extend: { 29 | boxShadow: { 30 | glow: "0 0 4px rgb(0 0 0 / 0.1)", 31 | }, 32 | maxWidth: { 33 | lg: "33rem", 34 | "2xl": "40rem", 35 | "3xl": "50rem", 36 | "5xl": "66rem", 37 | }, 38 | opacity: { 39 | 1: "0.01", 40 | 2.5: "0.025", 41 | 7.5: "0.075", 42 | 15: "0.15", 43 | }, 44 | }, 45 | }, 46 | plugins: [typographyPlugin, headlessuiPlugin], 47 | } satisfies Config; 48 | -------------------------------------------------------------------------------- /crates/cli/src/bin/cmd/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod default; 2 | pub mod generate; 3 | pub mod new; 4 | use std::process::exit; 5 | 6 | use anyhow::Result; 7 | use cargo_ngyn::CmdExit; 8 | use clap::ArgMatches; 9 | use console::Style; 10 | use tracing::debug; 11 | use tracing::metadata::LevelFilter; 12 | use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt; 13 | use tracing_subscriber::util::SubscriberInitExt; 14 | use tracing_subscriber::{EnvFilter, Registry}; 15 | 16 | pub fn tracing(matches: &ArgMatches) { 17 | let level = if matches.get_flag("verbose") { 18 | LevelFilter::INFO 19 | } else { 20 | LevelFilter::OFF 21 | }; 22 | Registry::default() 23 | .with(tracing_tree::HierarchicalLayer::new(2)) 24 | .with( 25 | EnvFilter::builder() 26 | .with_default_directive(level.into()) 27 | .with_env_var("LOG") 28 | .from_env_lossy(), 29 | ) 30 | .init(); 31 | } 32 | 33 | const DEFAULT_ERR_EXIT_CODE: i32 = 1; 34 | pub fn result_exit(res: Result) { 35 | let exit_with = match res { 36 | Ok(cmd) => { 37 | if let Some(message) = cmd.message { 38 | let style = if exitcode::is_success(cmd.code) { 39 | Style::new().green() 40 | } else { 41 | Style::new().red() 42 | }; 43 | eprintln!("{}", style.apply_to(message)); 44 | } 45 | cmd.code 46 | } 47 | Err(e) => { 48 | debug!("{:?}", e); 49 | DEFAULT_ERR_EXIT_CODE 50 | } 51 | }; 52 | exit(exit_with) 53 | } 54 | -------------------------------------------------------------------------------- /examples/vercel_app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ngyn 7 | 11 | 12 | 13 |

Welcome to Ngyn

14 |

15 | Ngyn is a progressive backend framework written in Rust. It's designed to 16 | be fast, reliable, and efficient, making it ideal for high-performance web 17 | applications. 18 |

19 |

Installation

20 |

21 | To install Ngyn, you need to have Rust installed on your machine. Once you 22 | have Rust installed, you can install Ngyn using cargo: 23 |

24 |
cargo install ngyn
25 |

Capabilities

26 |

27 | Ngyn can handle routing, middleware, and async interactions. It's designed 28 | to be extensible, allowing you to add your own functionality as needed. 29 |

30 |

Limitations

31 |

32 | While Ngyn is powerful, it's not a silver bullet. It's not designed to 33 | handle frontend logic or user interface design. For those tasks, you'll 34 | need to use a frontend framework or library. 35 |

36 | See a route served with Ngyn on the Vercel platform 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/graphql_app/src/main.rs: -------------------------------------------------------------------------------- 1 | use juniper::{ 2 | tests::fixtures::starwars::schema::{Database, Query}, 3 | EmptyMutation, EmptySubscription, RootNode, 4 | }; 5 | use juniper_hyper::{graphiql, graphql, playground}; 6 | use ngyn::prelude::*; 7 | use ngyn_hyper::HyperApplication; 8 | use std::sync::Arc; 9 | 10 | #[handler] 11 | fn home() -> &'static str { 12 | "You can access the GraphQL playground at /playground or the GraphiQL interface at /graphiql." 13 | } 14 | 15 | #[handler] 16 | async fn handle_graphql(req: NgynRequest) -> String { 17 | let db = Arc::new(Database::new()); 18 | let root_node = Arc::new(RootNode::new( 19 | Query, 20 | EmptyMutation::::new(), 21 | EmptySubscription::::new(), 22 | )); 23 | let graphql_res = graphql(root_node, db, req.map(|_b| panic!(""))).await; 24 | graphql_res.body().as_str().to_owned() 25 | } 26 | 27 | #[handler] 28 | async fn handle_graphiql() -> String { 29 | let graphiql_res = graphiql("/graphql", None).await; 30 | graphiql_res.body().as_str().to_owned() 31 | } 32 | 33 | #[handler] 34 | async fn handle_playground() -> String { 35 | let playground_res = playground("/graphql", None).await; 36 | playground_res.body().as_str().to_owned() 37 | } 38 | 39 | #[tokio::main] 40 | async fn main() { 41 | let mut app = HyperApplication::default(); 42 | 43 | app.get("/", home); 44 | 45 | app.get("/playground", async_wrap(handle_playground)); 46 | 47 | app.get("/graphiql", async_wrap(handle_graphiql)); 48 | 49 | app.any("/graphql", async_wrap(handle_graphql)); 50 | 51 | println!("Listening on http://127.0.0.1:8080"); 52 | 53 | let _ = app.listen("127.0.0.1:8080").await; 54 | } 55 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.4/foundations/gates.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Gates 6 | 7 | Gates are a way to protect your routes from unauthorized access. They are injectables which can be used to perform authentication, authorization, or other checks before allowing a request to proceed. 8 | 9 | ## Defining Gates 10 | 11 | To define a gate, you need to implement the `NgynGate` trait for the component. This trait provides a method called `can_activate` that is responsible for checking if the request should be allowed to proceed. 12 | 13 | ```rust 14 | use ngyn::prelude::*; 15 | 16 | #[injectable] 17 | struct AuthGate; 18 | 19 | impl NgynGate for AuthGate { 20 | async fn can_activate(&self, cx: &mut NgynContext, _res: &mut NgynResponse) -> bool { 21 | // Check if the request has a valid token 22 | cx.request().headers().get("Authorization").is_some() 23 | } 24 | } 25 | ``` 26 | 27 | ## Using Gates 28 | 29 | ### Controller-Level Gates 30 | 31 | To use a gate, you need to apply it to your routes using the `#[check]` attribute macro. 32 | 33 | ```rust 34 | use ngyn::prelude::*; 35 | 36 | #[controller] 37 | struct MyController; 38 | 39 | #[routes] 40 | #[check(AuthGate)] 41 | impl MyController { 42 | #[get("/")] 43 | fn index(&self) -> String { 44 | "Hello World!".to_string() 45 | } 46 | } 47 | ``` 48 | 49 | ### Route-Level Gates 50 | 51 | You can also apply a gate to a specific route by using the `#[check]` attribute macro on the route method. 52 | 53 | ```rust 54 | use ngyn::prelude::*; 55 | 56 | #[controller] 57 | struct MyController; 58 | 59 | #[routes] 60 | impl MyController { 61 | #[get("/")] 62 | #[check(AuthGate)] 63 | fn index(&self) -> String { 64 | "Hello World!".to_string() 65 | } 66 | } 67 | ``` 68 | -------------------------------------------------------------------------------- /sites/docs/src/components/Tag.tsx: -------------------------------------------------------------------------------- 1 | import clsx from "clsx"; 2 | 3 | const variantStyles = { 4 | small: "", 5 | medium: "rounded-lg px-1.5 ring-1 ring-inset", 6 | }; 7 | 8 | const colorStyles = { 9 | orange: { 10 | small: "text-orange-500 dark:text-orange-400", 11 | medium: 12 | "ring-orange-300 dark:ring-orange-400/30 bg-orange-400/10 text-orange-500 dark:text-orange-400", 13 | }, 14 | sky: { 15 | small: "text-sky-500", 16 | medium: 17 | "ring-sky-300 bg-sky-400/10 text-sky-500 dark:ring-sky-400/30 dark:bg-sky-400/10 dark:text-sky-400", 18 | }, 19 | amber: { 20 | small: "text-amber-500", 21 | medium: 22 | "ring-amber-300 bg-amber-400/10 text-amber-500 dark:ring-amber-400/30 dark:bg-amber-400/10 dark:text-amber-400", 23 | }, 24 | rose: { 25 | small: "text-red-500 dark:text-rose-500", 26 | medium: 27 | "ring-rose-200 bg-rose-50 text-red-500 dark:ring-rose-500/20 dark:bg-rose-400/10 dark:text-rose-400", 28 | }, 29 | zinc: { 30 | small: "text-zinc-400 dark:text-zinc-500", 31 | medium: 32 | "ring-zinc-200 bg-zinc-50 text-zinc-500 dark:ring-zinc-500/20 dark:bg-zinc-400/10 dark:text-zinc-400", 33 | }, 34 | }; 35 | 36 | const valueColorMap = { 37 | GET: "orange", 38 | POST: "sky", 39 | PUT: "amber", 40 | DELETE: "rose", 41 | } as Record; 42 | 43 | export function Tag({ 44 | children, 45 | variant = "medium", 46 | color = valueColorMap[children] ?? "orange", 47 | }: { 48 | children: keyof typeof valueColorMap & (string | {}); 49 | variant?: keyof typeof variantStyles; 50 | color?: keyof typeof colorStyles; 51 | }) { 52 | return ( 53 | 60 | {children} 61 | 62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /crates/cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-ngyn" 3 | version = "0.0.1" 4 | edition = "2021" 5 | description = "Modular backend framework for web applications" 6 | license = "MIT" 7 | documentation = "https://ngyn.rs/docs" 8 | repository = "https://github.com/ngyn-rs/ngyn" 9 | homepage = "https://ngyn.rs" 10 | rust-version = "1.81.0" 11 | 12 | [lib] 13 | path = "src/lib.rs" 14 | 15 | # 16 | # cargo binstall metadata: 17 | # 18 | 19 | [package.metadata.binstall.overrides.x86_64-pc-windows-msvc] 20 | pkg-url = "{ repo }/releases/download/v{ version }/ngyn-x86_64-windows.zip" 21 | bin-dir = "ngyn-x86_64-windows/ngyn.exe" 22 | pkg-fmt = "zip" 23 | 24 | [package.metadata.binstall.overrides.x86_64-apple-darwin] 25 | pkg-url = "{ repo }/releases/download/v{ version }/ngyn-x86_64-macos.tar.xz" 26 | bin-dir = "ngyn-x86_64-macos/ngyn" 27 | pkg-fmt = "txz" 28 | 29 | [package.metadata.binstall.overrides.aarch64-apple-darwin] 30 | pkg-url = "{ repo }/releases/download/v{ version }/ngyn-aarch64-macos.tar.xz" 31 | bin-dir = "ngyn-x86_64-macos/ngyn" 32 | pkg-fmt = "txz" 33 | 34 | [package.metadata.binstall.overrides.x86_64-unknown-linux-gnu] 35 | pkg-url = "{ repo }/releases/download/v{ version }/ngyn-x86_64-linux.tar.xz" 36 | bin-dir = "ngyn-x86_64-linux/ngyn" 37 | pkg-fmt = "txz" 38 | 39 | # 40 | 41 | [dependencies] 42 | 43 | clap = { version = "4.5", features = ["cargo"], optional = true } 44 | convert_case = "0.6" 45 | dialoguer = "0.10" 46 | serde = "1.0" 47 | anyhow = "1" 48 | console = "^0.15.0" 49 | lazy_static = "1" 50 | ramhorns = "1" 51 | exitcode = "^1.1.2" 52 | tracing = "^0.1.34" 53 | tracing-tree = { version = "0.2.1" } 54 | tracing-subscriber = { version = "0.3.1", features = ["env-filter"] } 55 | include_dir = "0.7.4" 56 | 57 | 58 | [features] 59 | default = ["cli"] 60 | # list optionals here: 61 | cli = ["clap"] 62 | 63 | [[bin]] 64 | name = "ngyn" 65 | path = "src/bin/main.rs" 66 | 67 | required-features = ["cli"] 68 | -------------------------------------------------------------------------------- /sites/docs/versioned_docs/version-0.5.x/gates.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Access Gates 6 | 7 | Access gates are a way to control access to your routes based on certain conditions. They are similar to middleware but are used to protect routes from unauthorized access. 8 | 9 | ## Creating an Access Gate 10 | 11 | To create an access gate, you need to define a struct that implements the `NgynGate` trait. The `NgynGate` trait has an associated async function `can_activate` that takes a `NgynContext` as an argument and returns a `bool`. 12 | 13 | If an error occurs while checking the condition, you should always return `false` to prevent access to the route, then you can further send a response with the error message. 14 | 15 | Here's an example of a simple access gate that checks if the request has a valid API key: 16 | 17 | ```rust 18 | use ngyn::prelude::*; 19 | 20 | struct ApiKeyGate; 21 | 22 | impl NgynGate for ApiKeyGate { 23 | async fn can_activate(ctx: NgynContext) -> bool { 24 | let api_key = ctx.request().headers().get("x-api-key"); 25 | 26 | match api_key { 27 | Some(key) if key == "secret" => true, 28 | _ => { 29 | *ctx.response_mut().status_mut() = StatusCode::FORBIDDEN; 30 | false 31 | }, 32 | } 33 | } 34 | } 35 | ``` 36 | 37 | ## Using Access Gates 38 | 39 | Access gates are route specific and can be added to the route handler using the `gates` option in the `#[handler]` attribute. 40 | 41 | Here's an example of using the `ApiKeyGate` access gate for a specific route: 42 | 43 | ```rust 44 | #[handler(gates = [ApiKeyGate])] 45 | fn protected_route() -> &'static str { 46 | "This is a protected route" 47 | } 48 | ``` 49 | 50 | In this example, the `protected_route` handler will only be accessible if the `ApiKeyGate` access gate returns `true`. If the gate returns `false`, the request will be rejected with a `403 Forbidden` response. 51 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to ngyn 2 | 3 | ## Welcome! 4 | 5 | First off, thank you for considering contributing to ngyn! It's people like you that make ngyn such a great tool. 6 | 7 | ## Code of Conduct 8 | 9 | Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project, you agree to abide by its terms. 10 | 11 | ## How Can I Contribute? 12 | 13 | ### Reporting Bugs 14 | 15 | - Use the GitHub Issues section 16 | - Provide a clear and descriptive title 17 | - Describe the exact steps to reproduce the problem 18 | - Include your operating system, Rust version, and ngyn version or commit hash 19 | 20 | ### Suggesting Enhancements 21 | 22 | - Open a GitHub Issue 23 | - Provide a clear and detailed explanation of the suggestion 24 | - Explain why this enhancement would be useful to most ngyn users 25 | 26 | ### Pull Requests 27 | 28 | 1. Fork the repository 29 | 2. Create a new branch for your feature or bugfix 30 | ```bash 31 | git checkout -b feature/your-feature-name 32 | ``` 33 | 3. Make your changes 34 | 4. Write or update tests 35 | 5. Ensure all tests pass 36 | ```bash 37 | cargo test 38 | ``` 39 | 6. Commit your changes with a descriptive commit message 40 | 7. Push to your fork and submit a pull request 41 | 42 | ### Development Setup 43 | 44 | 1. Install Rust (https://rustup.rs/) 45 | 2. Clone the repository 46 | ```bash 47 | git clone https://github.com/ngyn-rs/ngyn.git 48 | cd ngyn 49 | ``` 50 | 3. Install development dependencies 51 | ```bash 52 | cargo build 53 | ``` 54 | 55 | ## Development Workflow 56 | 57 | - Follow Rust's official style guidelines 58 | - update examples if necessary 59 | - Run `cargo fmt` before committing 60 | - Ensure `cargo clippy` passes with no warnings 61 | - Update Changelog for each crate that is modified 62 | - Write tests for new features and bug fixes 63 | 64 | ## Questions? 65 | 66 | If you have any questions, please open an issue or reach out to the maintainers. 67 | 68 | ## Thank You! 69 | 70 | Your contributions make open source amazing. Thank you for your help! -------------------------------------------------------------------------------- /sites/docs/src/theme/Layout/index.tsx: -------------------------------------------------------------------------------- 1 | import clsx from "clsx"; 2 | import ErrorBoundary from "@docusaurus/ErrorBoundary"; 3 | import { 4 | PageMetadata, 5 | SkipToContentFallbackId, 6 | ThemeClassNames, 7 | } from "@docusaurus/theme-common"; 8 | import { useKeyboardNavigation } from "@docusaurus/theme-common/internal"; 9 | import SkipToContent from "@theme/SkipToContent"; 10 | import AnnouncementBar from "@theme/AnnouncementBar"; 11 | import Navbar from "@theme/Navbar"; 12 | // import Footer from "@theme/Footer"; 13 | import LayoutProvider from "@theme/Layout/Provider"; 14 | import ErrorPageContent from "@theme/ErrorPageContent"; 15 | import type { Props } from "@theme/Layout"; 16 | import styles from "./styles.module.css"; 17 | import { SectionProvider } from "@site/src/components/SectionProvider"; 18 | import { Footer } from "@site/src/components/Footer"; 19 | import { Toaster } from "react-hot-toast"; 20 | 21 | export default function Layout(props: Props): JSX.Element { 22 | const { 23 | children, 24 | noFooter, 25 | wrapperClassName, 26 | // Not really layout-related, but kept for convenience/retro-compatibility 27 | title, 28 | description, 29 | } = props; 30 | 31 | useKeyboardNavigation(); 32 | 33 | return ( 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
52 | } 54 | > 55 | {children} 56 | 57 |
58 | 59 | {!noFooter &&