Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 156 additions & 60 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ serde_json = "1"
http = "1"
anyhow = "1"
thiserror = "2"
validator = { version = "0.16", features = ["derive"] }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if validator is actually used in nestforge-config or nestforge-core
rg -l 'validator' --type=toml -g '*/nestforge-config/*' -g '*/nestforge-core/*'
rg 'use.*validator|validator::' --type=rust

Repository: vernonthedev/nestforge

Length of output: 98


🏁 Script executed:

# Check Cargo.toml files to see where validator is declared
cat crates/nestforge-core/Cargo.toml | grep -A 2 -B 2 validator

# Also search for any validator references in the entire Rust codebase
rg 'validator' --type=rust -i

Repository: vernonthedev/nestforge

Length of output: 179


🏁 Script executed:

# Search for any usage of validator in the entire Rust codebase
rg 'validator' --type=rust

# Also check if it's only declared but not used (check for imports more broadly)
rg -i 'validate|validator' --type=rust -A 2

Repository: vernonthedev/nestforge

Length of output: 14611


🏁 Script executed:

# Confirm by checking the workspace Cargo.toml to see if validator is truly a workspace dependency
cat Cargo.toml | grep -A 5 "\[workspace"

Repository: vernonthedev/nestforge

Length of output: 639


🏁 Script executed:

# Check if validator is in workspace.dependencies section
cat Cargo.toml | grep -A 50 "\[workspace.dependencies\]" | grep -i validator

# Also check the full nestforge-core Cargo.toml dependencies section
cat crates/nestforge-core/Cargo.toml | grep -A 30 "\[dependencies\]"

Repository: vernonthedev/nestforge

Length of output: 432


Remove unused validator dependency.

The validator crate is declared as a workspace dependency but is never used. The codebase implements a custom validation system with the Validate trait and #[derive(Validate)] macro instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Cargo.toml` at line 48, Remove the unused workspace dependency declaration
for the validator crate (the line declaring validator = { version = "0.16",
features = ["derive"] } in Cargo.toml) since the project uses its own Validate
trait and custom #[derive(Validate)] implementation; delete that dependency
entry from Cargo.toml and run cargo update / cargo metadata to ensure the
workspace lockfile and dependency tree are cleaned up.

sha2 = "0.10"
tower = "0.5"
tower-http = { version = "0.6", features = ["trace", "cors"] }
Expand Down
33 changes: 7 additions & 26 deletions crates/nestforge-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2724,35 +2724,16 @@ impl HealthController {
.to_string()
}

fn template_app_config_rs(transport: AppTransport) -> String {
let default_app_name = match transport {
AppTransport::Http => "NestForge HTTP",
AppTransport::Graphql => "NestForge GraphQL",
AppTransport::Grpc => "NestForge gRPC",
AppTransport::Websockets => "NestForge WebSockets",
AppTransport::Microservices => "NestForge Microservices",
};

format!(
r#"use nestforge::{{prelude::*, ConfigModule, ConfigOptions}};

#[injectable(factory = load_app_config)]
pub struct AppConfig {{
pub app_name: String,
}}
fn template_app_config_rs(_transport: AppTransport) -> String {
r#"use nestforge_config::{ConfigService, ConfigModule};

fn load_app_config() -> anyhow::Result<AppConfig> {{
Ok(ConfigModule::for_root::<AppConfig>(ConfigOptions::new().env_file(".env"))?)
}}
pub type AppConfig = ConfigService;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep scaffolded AppConfig injectable

This changes the generated AppConfig provider into a plain alias of ConfigService, but template_app_module_rs still emits providers = [AppConfig] (crates/nestforge-cli/src/main.rs:2571-2600), and the module macro expands bare type names to register_injectable::<T> (crates/nestforge-macros/src/lib.rs:1749-1755). Any project created with nestforge new will therefore fail to compile because ConfigService does not implement Injectable.

Useful? React with 👍 / 👎.


impl nestforge::FromEnv for AppConfig {{
fn from_env(env: &nestforge::EnvStore) -> Result<Self, nestforge::ConfigError> {{
let app_name = env.get("APP_NAME").unwrap_or("{default_app_name}").to_string();
Ok(Self {{ app_name }})
}}
}}
pub fn load_config() -> AppConfig {
ConfigModule::for_root_with_options(ConfigModule::for_root().env_file(".env"))
}
Comment on lines +2727 to +2734
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Scaffolded app won't compile 🐞 Bug ✓ Correctness

The CLI now generates app_config.rs that imports nestforge_config directly and defines
AppConfig = ConfigService, but the generated Cargo.toml doesn’t add a nestforge-config
dependency and other templates still register/consume AppConfig as if it were a struct
(register_injectable::<AppConfig>, config.app_name), causing immediate compilation failures in
new projects.
Agent Prompt
### Issue description
`nestforge new` scaffolding is internally inconsistent after switching `AppConfig` to a `ConfigService` alias. The generated project will not compile.

### Issue Context
- `#[module]` providers accept expressions; a bare path like `AppConfig` is treated as an injectable type and becomes `register_injectable::<AppConfig>`.
- With `pub type AppConfig = ConfigService;`, this is not injectable.

### Fix Focus Areas
- In `template_app_config_rs`, import from `nestforge` (re-export) instead of `nestforge_config`, OR add an explicit `nestforge-config` dependency in generated Cargo.toml.
- In `template_app_module_rs`, register config as a value/factory expression, e.g. `providers = [load_config()]` or `providers = [Provider::value(load_config())]`.
- Update `template_main_rs` (GraphQL) to use getters (e.g. `config.get_string_or("APP_NAME", ...)`) instead of `config.app_name`.
- Update `template_app_service_rs` to use the new API (resolve `AppConfig`/`ConfigService` from container and call getters), removing `FromEnv` + `EnvStore::load_with_options`.

### Fix Focus Areas (locations)
- crates/nestforge-cli/src/main.rs[2289-2342]
- crates/nestforge-cli/src/main.rs[2560-2604]
- crates/nestforge-cli/src/main.rs[2680-2705]
- crates/nestforge-cli/src/main.rs[2390-2405]
- crates/nestforge-cli/src/main.rs[2727-2736]
- crates/nestforge-macros/src/lib.rs[1749-1756]
- crates/nestforge-macros/src/lib.rs[1783-1792]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

"#
)
.to_string()
Comment on lines +2727 to +2736
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

This generated app_config.rs no longer matches the rest of the scaffold.

Line 2728 switches the template to nestforge_config, but template_app_cargo_toml() still only adds nestforge as a dependency. More importantly, the other generated files in this same source file still treat AppConfig as a struct/FromEnv type (template_app_service_rs reads config.app_name, and template_app_module_rs never calls load_config()), so fresh projects will not compile or load .env through this path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/nestforge-cli/src/main.rs` around lines 2727 - 2736, The generated
app_config.rs template (template_app_config_rs) now imports nestforge_config and
exposes load_config/ConfigService, which is inconsistent with
template_app_service_rs and template_app_module_rs that expect AppConfig as a
struct/FromEnv with fields like config.app_name and do not call load_config; fix
by making the templates consistent: either (A) revert template_app_config_rs to
generate the original AppConfig struct implementing FromEnv with the expected
fields (e.g., app_name) so it matches template_app_service_rs and keep
template_app_cargo_toml depending on nestforge, or (B) update
template_app_cargo_toml to add nestforge_config and modify
template_app_service_rs and template_app_module_rs to call load_config() and use
the ConfigService API; pick one approach and apply it across
template_app_config_rs, template_app_cargo_toml, template_app_service_rs, and
template_app_module_rs so all references to AppConfig/load_config/nestforge vs
nestforge_config are consistent.

}

fn template_graphql_schema_rs() -> String {
Expand Down
1 change: 1 addition & 0 deletions crates/nestforge-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ categories = ["config"]

[dependencies]
thiserror = { workspace = true }
dotenvy = "0.15"
60 changes: 57 additions & 3 deletions crates/nestforge-config/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,60 @@
# nestforge-config

Environment configuration loading and validation utilities for NestForge.
A NestJS-inspired configuration module for NestForge applications.

- Repository: https://github.com/vernonthedev/nestforge
- Docs: https://github.com/vernonthedev/nestforge/wiki
## Features

- **Type-safe configuration** - Get typed values from environment variables
- **NestJS-like API** - Familiar patterns for NestJS developers
- **DI Integration** - Seamlessly works with NestForge's dependency injection
- **Simple getters** - No unwrap_or chains, just `get_string_or("KEY", "default")`
- **Dotenv support** - Automatic loading from `.env` files

## Quick Start

```rust
use nestforge_config::{ConfigService, ConfigModule};

pub type AppConfig = ConfigService;

pub fn load_config() -> AppConfig {
ConfigModule::for_root_with_options(ConfigModule::for_root().env_file(".env"))
}
Comment on lines +20 to +22
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the actual for_root method signature and usage
ast-grep --pattern 'fn for_root($$$) -> $_ {
  $$$
}'

# Check how ConfigOptions is created
rg 'ConfigModule::for_root\(' --type=rust
rg 'ConfigOptions::new\(\)' --type=rust

Repository: vernonthedev/nestforge

Length of output: 1225


🏁 Script executed:

# Check the ConfigModule::for_root() method signature and what it returns
ast-grep --pattern 'impl ConfigModule {
  $$$
  fn for_root($$$) {
    $$$
  }
  $$$
}'

# Also check for ConfigModule::for_root_with_options signature
ast-grep --pattern 'fn for_root_with_options($$$) {
  $$$
}'

Repository: vernonthedev/nestforge

Length of output: 48


🏁 Script executed:

# Get the README.md content around lines 20-22 for full context
sed -n '15,30p' crates/nestforge-config/README.md

Repository: vernonthedev/nestforge

Length of output: 373


🏁 Script executed:

# Check what ConfigModule::for_root() actually returns by looking at the implementation
rg -A 5 'fn for_root\(' crates/nestforge-config/src/lib.rs --type=rust

Repository: vernonthedev/nestforge

Length of output: 308


Standardize configuration initialization pattern across documentation and examples.

The example code is correct—ConfigModule::for_root() returns ConfigOptions, and calling .env_file(".env") on it works as expected. However, the README uses ConfigModule::for_root().env_file(".env") while all example projects use ConfigOptions::new().env_file(".env") directly (both patterns are functionally equivalent since for_root() internally calls ConfigOptions::new()). For consistency and clarity, align the README to use the same pattern as the actual examples.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/nestforge-config/README.md` around lines 20 - 22, Update the README's
example to match the project's standard initialization pattern by replacing the
use of ConfigModule::for_root().env_file(".env") with the
ConfigOptions::new().env_file(".env") pattern shown in examples; specifically,
in the load_config() example (returning AppConfig) use ConfigOptions::new() to
build the options (instead of calling ConfigModule::for_root()) so the docs
align with examples and the code paths that ultimately call
ConfigOptions::new().

```

## Usage in Services

```rust
use nestforge::prelude::*;

pub struct MyService {
config: Config<AppConfig>,
}

impl MyService {
pub fn do_something(&self) {
let app_name = self.config.get_string_or("APP_NAME", "My App");
let port = self.config.get_u16_or("PORT", 3000);
let debug = self.config.get_bool_or("DEBUG", false);
}
}
```

## Available Methods

| Method | Description |
|--------|-------------|
| `get_string("KEY")` | Get string (default: `""`) |
| `get_string_or("KEY", "default")` | Get with default |
| `get_u16("KEY")` | Get u16 (default: `0`) |
| `get_u16_or("KEY", 3000)` | Get with default |
| `get_bool("KEY")` | Get bool (default: `false`) |
| `get_bool_or("KEY", true)` | Get with default |
| `get("KEY")` | Get `Option<&str>` |
| `has("KEY")` | Check if key exists |

## Resources

- [Documentation](https://github.com/vernonthedev/nestforge/wiki)
- [Examples](https://github.com/vernonthedev/nestforge/tree/main/examples)
- [Discord Community](https://discord.gg/nestforge)
Loading
Loading